/*-------------------------------------------------------------------------*/
/* Copyright 2010-2021 Armin Biere Johannes Kepler University Linz         */
/* Copyright 2021-2022 Armin Biere Albert-Ludwigs University Freiburg      */
/*-------------------------------------------------------------------------*/

#include "lglib.h"

/*------------------------------------------------------------------------*/

#include "lglconst.h"
#include "lglopts.h"

/*-------------------------------------------------------------------------*/

#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <unistd.h>

/*-------------------------------------------------------------------------*/

#ifndef NLGLYALSAT
#include "yals.h"
#endif

#ifndef NLGLDRUPLIG
#include "druplig.h"
#endif

/*------------------------------------------------------------------------*/
#ifndef NLGLOG
/*------------------------------------------------------------------------*/

#define MAPLOGLEVEL(LEVEL) (LEVEL)

#define LOG(LEVEL, FMT, ARGS...)                                                                                       \
	do {                                                                                                               \
		if (MAPLOGLEVEL(LEVEL) > lgl->opts->log.val)                                                                   \
			break;                                                                                                     \
		lglogstart(lgl, MAPLOGLEVEL(LEVEL), FMT, ##ARGS);                                                              \
		lglogend(lgl);                                                                                                 \
	} while (0)

#define LOGCLS(LEVEL, CLS, FMT, ARGS...)                                                                               \
	do {                                                                                                               \
		const int* P;                                                                                                  \
		if (MAPLOGLEVEL(LEVEL) > lgl->opts->log.val)                                                                   \
			break;                                                                                                     \
		lglogstart(lgl, MAPLOGLEVEL(LEVEL), FMT, ##ARGS);                                                              \
		for (P = (CLS); *P; P++)                                                                                       \
			fprintf(lgl->out, " %d", *P);                                                                              \
		lglogend(lgl);                                                                                                 \
	} while (0)

#define LOGMCLS(LEVEL, CLS, FMT, ARGS...)                                                                              \
	do {                                                                                                               \
		const int* P;                                                                                                  \
		if (MAPLOGLEVEL(LEVEL) > lgl->opts->log.val)                                                                   \
			break;                                                                                                     \
		lglogstart(lgl, MAPLOGLEVEL(LEVEL), FMT, ##ARGS);                                                              \
		for (P = (CLS); *P; P++)                                                                                       \
			fprintf(lgl->out, " %d", lglm2i(lgl, *P));                                                                 \
		lglogend(lgl);                                                                                                 \
	} while (0)

#define LOGRESOLVENT(LEVEL, FMT, ARGS...)                                                                              \
	do {                                                                                                               \
		const int* P;                                                                                                  \
		if (MAPLOGLEVEL(LEVEL) > lgl->opts->log.val)                                                                   \
			break;                                                                                                     \
		lglogstart(lgl, MAPLOGLEVEL(LEVEL), FMT, ##ARGS);                                                              \
		for (P = lgl->resolvent.start; P < lgl->resolvent.top; P++)                                                    \
			fprintf(lgl->out, " %d", *P);                                                                              \
		lglogend(lgl);                                                                                                 \
	} while (0)

#define LOGREASON(LEVEL, LIT, REASON0, REASON1, FMT, ARGS...)                                                          \
	do {                                                                                                               \
		int TAG, TMP, RED, G;                                                                                          \
		const int *C, *P;                                                                                              \
		if (MAPLOGLEVEL(LEVEL) > lgl->opts->log.val)                                                                   \
			break;                                                                                                     \
		lglogstart(lgl, MAPLOGLEVEL(LEVEL), FMT, ##ARGS);                                                              \
		TMP = ((REASON0) >> RMSHFT);                                                                                   \
		RED = ((REASON0) & REDCS);                                                                                     \
		TAG = ((REASON0) & MASKCS);                                                                                    \
		if (TAG == DECISION)                                                                                           \
			fputs(" decision", lgl->out);                                                                              \
		else if (TAG == UNITCS)                                                                                        \
			fprintf(lgl->out, " unit %d", (LIT));                                                                      \
		else if (TAG == BINCS) {                                                                                       \
			fprintf(lgl->out, " %s binary clause %d %d", lglred2str(RED), (LIT), TMP);                                 \
		} else if (TAG == TRNCS) {                                                                                     \
			fprintf(lgl->out, " %s ternary clause %d %d %d", lglred2str(RED), (LIT), TMP, (REASON1));                  \
		} else {                                                                                                       \
			assert(TAG == LRGCS);                                                                                      \
			C = lglidx2lits(lgl, RED, (REASON1));                                                                      \
			for (P = C; *P; P++)                                                                                       \
				;                                                                                                      \
			fprintf(lgl->out, " size %ld", (long)(P - C));                                                             \
			if (RED) {                                                                                                 \
				G = ((REASON1) & GLUEMASK);                                                                            \
				fprintf(lgl->out, " glue %d redundant", G);                                                            \
			} else                                                                                                     \
				fputs(" irredundant", lgl->out);                                                                       \
			fputs(" clause", lgl->out);                                                                                \
			for (P = C; *P; P++) {                                                                                     \
				fprintf(lgl->out, " %d", *P);                                                                          \
			}                                                                                                          \
		}                                                                                                              \
		lglogend(lgl);                                                                                                 \
	} while (0)

#define LOGDSCHED(LEVEL, LIT, FMT, ARGS...)                                                                            \
	do {                                                                                                               \
		int POS;                                                                                                       \
		if (MAPLOGLEVEL(LEVEL) > lgl->opts->log.val)                                                                   \
			break;                                                                                                     \
		POS = *lgldpos(lgl, LIT);                                                                                      \
		lglogstart(lgl, MAPLOGLEVEL(LEVEL), "dsched[%d] = %d ", POS, LIT);                                             \
		fprintf(lgl->out, FMT, ##ARGS);                                                                                \
		fprintf(lgl->out, " score %s", lglscr2str(lgl, lglqvar(lgl, LIT)->score));                                     \
		lglogend(lgl);                                                                                                 \
	} while (0)

#define LOGESCHED(LEVEL, LIT, FMT, ARGS...)                                                                            \
	do {                                                                                                               \
		int POS;                                                                                                       \
		EVar* EV;                                                                                                      \
		if (MAPLOGLEVEL(LEVEL) > lgl->opts->log.val)                                                                   \
			break;                                                                                                     \
		POS = *lglepos(lgl, LIT);                                                                                      \
		EV = lglevar(lgl, LIT);                                                                                        \
		lglogstart(lgl, MAPLOGLEVEL(LEVEL), "esched[%d] = %d ", POS, LIT);                                             \
		fprintf(lgl->out, FMT, ##ARGS);                                                                                \
		fprintf(lgl->out, " score");                                                                                   \
		fprintf(lgl->out, " occ %d %d", EV->occ[0], EV->occ[1]);                                                       \
		lglogend(lgl);                                                                                                 \
	} while (0)

#define LOGEQN(LEVEL, EQN, FMT, ARGS...)                                                                               \
	do {                                                                                                               \
		const int *P, *START;                                                                                          \
		if (MAPLOGLEVEL(LEVEL) > lgl->opts->log.val)                                                                   \
			break;                                                                                                     \
		lglogstart(lgl, MAPLOGLEVEL(LEVEL), FMT, ##ARGS);                                                              \
		START = lgl->gauss->xors.start + (EQN);                                                                        \
		assert(START < lgl->gauss->xors.top);                                                                          \
		for (P = START; *P > 1; P++)                                                                                   \
			fprintf(lgl->out, " %d", *P);                                                                              \
		fprintf(lgl->out, " = %d", *P);                                                                                \
		lglogend(lgl);                                                                                                 \
	} while (0)

/*------------------------------------------------------------------------*/
#else /* end of then start of else part of 'ifndef NLGLOG' */
/*------------------------------------------------------------------------*/

#define LOG(ARGS...)                                                                                                   \
	do {                                                                                                               \
	} while (0)
#define LOGCLS(ARGS...)                                                                                                \
	do {                                                                                                               \
	} while (0)
#define LOGMCLS(ARGS...)                                                                                               \
	do {                                                                                                               \
	} while (0)
#define LOGRESOLVENT(ARGS...)                                                                                          \
	do {                                                                                                               \
	} while (0)
#define LOGREASON(ARGS...)                                                                                             \
	do {                                                                                                               \
	} while (0)
#define LOGDSCHED(ARGS...)                                                                                             \
	do {                                                                                                               \
	} while (0)
#define LOGESCHED(ARGS...)                                                                                             \
	do {                                                                                                               \
	} while (0)
#define LOGEQN(ARGS...)                                                                                                \
	do {                                                                                                               \
	} while (0)

/*------------------------------------------------------------------------*/
#endif /* end of else part of 'ifndef NLGLOG' */
/*------------------------------------------------------------------------*/

#define ABORTIF(COND, FMT, ARGS...)                                                                                    \
	do {                                                                                                               \
		if (!(COND))                                                                                                   \
			break;                                                                                                     \
		fprintf(stderr, "*** API usage error of '%s' in '%s'", __FILE__, __FUNCTION__);                                \
		if (lgl && lgl->tid >= 0)                                                                                      \
			fprintf(stderr, " (tid %d)", lgl->tid);                                                                    \
		fputs(": ", stderr);                                                                                           \
		fprintf(stderr, FMT, ##ARGS);                                                                                  \
		fputc('\n', stderr);                                                                                           \
		fflush(stderr);                                                                                                \
		lglabort(lgl);                                                                                                 \
		exit(1);                                                                                                       \
	} while (0)

// Useful for using our 'sleeponabort' and other hooks 'on abort' ...

#ifndef NDEBUG
#define ASSERT(COND)                                                                                                   \
	do {                                                                                                               \
		if ((COND))                                                                                                    \
			break;                                                                                                     \
		fprintf(                                                                                                       \
			stderr, "liblgl.a: %s:%d: %s: Lingeling Assertion `%s' failed.", __FUNCTION__, __LINE__, __FILE__, #COND); \
		if (lgl && lgl->tid >= 0)                                                                                      \
			fprintf(stderr, " (tid %d)", lgl->tid);                                                                    \
		fputc('\n', stderr);                                                                                           \
		fflush(stderr);                                                                                                \
		lglabort(lgl);                                                                                                 \
		exit(1);                                                                                                       \
	} while (0)
#else
#define ASSERT(COND)                                                                                                   \
	do {                                                                                                               \
	} while (0)
#endif

#define COVER(COND)                                                                                                    \
	do {                                                                                                               \
		if (!(COND))                                                                                                   \
			break;                                                                                                     \
		fprintf(                                                                                                       \
			stderr, "liblgl.a: %s:%d: %s: Coverage target `%s' reached.", __FUNCTION__, __LINE__, __FILE__, #COND);    \
		if (lgl && lgl->tid >= 0)                                                                                      \
			fprintf(stderr, " (tid %d)", lgl->tid);                                                                    \
		fputc('\n', stderr);                                                                                           \
		fflush(stderr);                                                                                                \
		abort(); /* TODO: why not 'lglabort'? */                                                                       \
	} while (0)

#define REQINIT()                                                                                                      \
	do {                                                                                                               \
		ABORTIF(!lgl, "uninitialized manager");                                                                        \
	} while (0)

#define REQINITNOTFORKED()                                                                                             \
	do {                                                                                                               \
		REQINIT();                                                                                                     \
		ABORTIF(lgl->forked, "forked manager");                                                                        \
	} while (0)

#define REQUIRE(STATE)                                                                                                 \
	do {                                                                                                               \
		REQINIT();                                                                                                     \
		ABORTIF(!(lgl->state & (STATE)), "!(%s)", #STATE);                                                             \
	} while (0)

#define TRANS(STATE)                                                                                                   \
	do {                                                                                                               \
		assert(lgl->state != STATE);                                                                                   \
		LOG(1, "transition to state " #STATE);                                                                         \
		lgl->state = STATE;                                                                                            \
	} while (0)

/*------------------------------------------------------------------------*/

#if !defined(NDEBUG) || !defined(NLGLOG)
#define RESOLVENT
#endif

/*------------------------------------------------------------------------*/

#define TRAPI(MSG, ARGS...)                                                                                            \
	do {                                                                                                               \
		if (!lgl->apitrace)                                                                                            \
			break;                                                                                                     \
		lgltrapi(lgl, MSG, ##ARGS);                                                                                    \
	} while (0)

#define LGLCHKACT(ACT)                                                                                                 \
	do {                                                                                                               \
		assert(NOTALIT <= (ACT) && (ACT) < REMOVED - 1);                                                               \
	} while (0)

/*------------------------------------------------------------------------*/

#define NEW(P, N)                                                                                                      \
	do {                                                                                                               \
		(P) = lglnew(lgl, (N) * sizeof *(P));                                                                          \
	} while (0)

#define DEL(P, N)                                                                                                      \
	do {                                                                                                               \
		lgldel(lgl, (P), (N) * sizeof *(P));                                                                           \
		(P) = 0;                                                                                                       \
	} while (0)

#define RSZ(P, O, N)                                                                                                   \
	do {                                                                                                               \
		(P) = lglrsz(lgl, (P), (O) * sizeof *(P), (N) * sizeof *(P));                                                  \
	} while (0)

#define CLN(P, N)                                                                                                      \
	do {                                                                                                               \
		memset((P), 0, (N) * sizeof *(P));                                                                             \
	} while (0)

#define CLRPTR(P)                                                                                                      \
	do {                                                                                                               \
		memset((P), 0, sizeof *(P));                                                                                   \
	} while (0)

#define CLR(P)                                                                                                         \
	do {                                                                                                               \
		memset(&(P), 0, sizeof(P));                                                                                    \
	} while (0)

/*------------------------------------------------------------------------*/

#define SWAP(TYPE, A, B)                                                                                               \
	do {                                                                                                               \
		TYPE TMP = (A);                                                                                                \
		(A) = (B);                                                                                                     \
		(B) = TMP;                                                                                                     \
	} while (0)

#define ISORTLIM 10

#define CMPSWAP(TYPE, CMP, P, Q)                                                                                       \
	do {                                                                                                               \
		if (CMP(&(P), &(Q)) > 0)                                                                                       \
			SWAP(TYPE, P, Q);                                                                                          \
	} while (0)

#define QPART(TYPE, CMP, A, L, R)                                                                                      \
	do {                                                                                                               \
		TYPE PIVOT;                                                                                                    \
		int J = (R);                                                                                                   \
		I = (L) - 1;                                                                                                   \
		PIVOT = (A)[J];                                                                                                \
		for (;;) {                                                                                                     \
			while (CMP(&(A)[++I], &PIVOT) < 0)                                                                         \
				;                                                                                                      \
			while (CMP(&PIVOT, &(A)[--J]) < 0)                                                                         \
				if (J == (L))                                                                                          \
					break;                                                                                             \
			if (I >= J)                                                                                                \
				break;                                                                                                 \
			SWAP(TYPE, (A)[I], (A)[J]);                                                                                \
		}                                                                                                              \
		SWAP(TYPE, (A)[I], (A)[R]);                                                                                    \
	} while (0)

#define QSORT(TYPE, CMP, A, N)                                                                                         \
	do {                                                                                                               \
		int L = 0, R = (N) - 1, M, LL, RR, I;                                                                          \
		assert(lglmtstk(&lgl->sortstk));                                                                               \
		if (R - L <= ISORTLIM)                                                                                         \
			break;                                                                                                     \
		for (;;) {                                                                                                     \
			M = (L + R) / 2;                                                                                           \
			SWAP(TYPE, (A)[M], (A)[R - 1]);                                                                            \
			CMPSWAP(TYPE, CMP, (A)[L], (A)[R - 1]);                                                                    \
			CMPSWAP(TYPE, CMP, (A)[L], (A)[R]);                                                                        \
			CMPSWAP(TYPE, CMP, (A)[R - 1], (A)[R]);                                                                    \
			QPART(TYPE, CMP, (A), L + 1, R - 1);                                                                       \
			if (I - L < R - I) {                                                                                       \
				LL = I + 1;                                                                                            \
				RR = R;                                                                                                \
				R = I - 1;                                                                                             \
			} else {                                                                                                   \
				LL = L;                                                                                                \
				RR = I - 1;                                                                                            \
				L = I + 1;                                                                                             \
			}                                                                                                          \
			if (R - L > ISORTLIM) {                                                                                    \
				assert(RR - LL > ISORTLIM);                                                                            \
				lglpushstk(lgl, &lgl->sortstk, LL);                                                                    \
				lglpushstk(lgl, &lgl->sortstk, RR);                                                                    \
			} else if (RR - LL > ISORTLIM)                                                                             \
				L = LL, R = RR;                                                                                        \
			else if (!lglmtstk(&lgl->sortstk)) {                                                                       \
				R = lglpopstk(&lgl->sortstk);                                                                          \
				L = lglpopstk(&lgl->sortstk);                                                                          \
			} else                                                                                                     \
				break;                                                                                                 \
		}                                                                                                              \
	} while (0)

#define ISORT(TYPE, CMP, A, N)                                                                                         \
	do {                                                                                                               \
		TYPE PIVOT;                                                                                                    \
		int L = 0, R = (N) - 1, I, J;                                                                                  \
		for (I = R; I > L; I--)                                                                                        \
			CMPSWAP(TYPE, CMP, (A)[I - 1], (A)[I]);                                                                    \
		for (I = L + 2; I <= R; I++) {                                                                                 \
			J = I;                                                                                                     \
			PIVOT = (A)[I];                                                                                            \
			while (CMP(&PIVOT, &(A)[J - 1]) < 0) {                                                                     \
				(A)[J] = (A)[J - 1];                                                                                   \
				J--;                                                                                                   \
			}                                                                                                          \
			(A)[J] = PIVOT;                                                                                            \
		}                                                                                                              \
	} while (0)

#ifdef NDEBUG
#define CHKSORT(CMP, A, N)                                                                                             \
	do {                                                                                                               \
	} while (0)
#else
#define CHKSORT(CMP, A, N)                                                                                             \
	do {                                                                                                               \
		int I;                                                                                                         \
		for (I = 0; I < (N) - 1; I++)                                                                                  \
			assert(CMP(&(A)[I], &(A)[I + 1]) <= 0);                                                                    \
	} while (0)
#endif

#define SORT(TYPE, A, N, CMP)                                                                                          \
	do {                                                                                                               \
		TYPE* AA = (A);                                                                                                \
		const int NN = (N);                                                                                            \
		QSORT(TYPE, CMP, AA, NN);                                                                                      \
		ISORT(TYPE, CMP, AA, NN);                                                                                      \
		CHKSORT(CMP, AA, NN);                                                                                          \
	} while (0)

#define SORTSTK(TYPE, S, CMP)                                                                                          \
	do {                                                                                                               \
		TYPE* A = (S)->start;                                                                                          \
		const int N = lglcntstk(S);                                                                                    \
		SORT(TYPE, A, N, CMP);                                                                                         \
	} while (0)

/*------------------------------------------------------------------------*/

#define LGLPOPWTK(WTK, WRAG, LIT, OTHER, RED, REMOVED)                                                                 \
	do {                                                                                                               \
		assert(!lglmtwtk(WTK));                                                                                        \
		(WTK)->top--;                                                                                                  \
		(WRAG) = (WTK)->top->wrag;                                                                                     \
		(LIT) = (WTK)->top->lit;                                                                                       \
		(OTHER) = (WTK)->top->other;                                                                                   \
		(RED) = (WTK)->top->red ? REDCS : 0;                                                                           \
		(REMOVED) = (WTK)->top->removed;                                                                               \
	} while (0)

/*------------------------------------------------------------------------*/

#define CLONE(FIELD, SIZE)                                                                                             \
	do {                                                                                                               \
		NEW(lgl->FIELD, (SIZE));                                                                                       \
		memcpy(lgl->FIELD, orig->FIELD, (SIZE) * sizeof *(lgl->FIELD));                                                \
	} while (0)

#define CLONESTK(NAME)                                                                                                 \
	do {                                                                                                               \
		size_t COUNT = orig->NAME.top - orig->NAME.start;                                                              \
		size_t SIZE = orig->NAME.end - orig->NAME.start;                                                               \
		size_t BYTES = SIZE * sizeof *lgl->NAME.start;                                                                 \
		NEW(lgl->NAME.start, SIZE);                                                                                    \
		memcpy(lgl->NAME.start, orig->NAME.start, BYTES);                                                              \
		lgl->NAME.top = lgl->NAME.start + COUNT;                                                                       \
		lgl->NAME.end = lgl->NAME.start + SIZE;                                                                        \
	} while (0)

/*------------------------------------------------------------------------*/

#define INCSTEPS(NAME) ((lgl->stats->steps++), (lgl->stats->NAME++))

#define ADDSTEPS(NAME, INC) (assert((INC) >= 0), (lgl->stats->steps += (INC)), (lgl->stats->NAME += (INC)))

/*------------------------------------------------------------------------*/

#define LGLUPDPEN(NAME, SUCCESS)                                                                                       \
	do {                                                                                                               \
		assert(!lgl->limits->NAME.del.rem);                                                                            \
		if ((SUCCESS) && lgl->limits->NAME.pen)                                                                        \
			lgl->limits->NAME.pen--;                                                                                   \
		if (!(SUCCESS) && lgl->limits->NAME.pen < lgl->opts->penmax.val)                                               \
			lgl->limits->NAME.pen++;                                                                                   \
		if ((SUCCESS) && lgl->limits->NAME.del.cur)                                                                    \
			lgl->limits->NAME.del.cur /= 2;                                                                            \
		if (!(SUCCESS) && lgl->limits->NAME.del.cur < lgl->opts->delmax.val)                                           \
			lgl->limits->NAME.del.cur++;                                                                               \
		lgl->limits->NAME.del.rem = lgl->limits->NAME.del.cur;                                                         \
	} while (0)

/*-------------------------------------------------------------------------*/

#define RMSHFTLIT(LIT) ((int)(((unsigned)LIT) << RMSHFT))

/*-------------------------------------------------------------------------*/

#define LGLL long long

/*-------------------------------------------------------------------------*/

typedef enum Tag
{
	FREEVAR = 0,
	FIXEDVAR = 1,
	EQUIVAR = 2,
	ELIMVAR = 3,

	DECISION = 0,
	UNITCS = 1,
	OCCS = 1,
	BINCS = 2,
	TRNCS = 3,
	LRGCS = 4,
	MASKCS = 7,

	REDCS = 8,
	RMSHFT = 4,
} Tag;

typedef enum State
{
	UNUSED = (1 << 0),
	OPTSET = (1 << 1),
	USED = (1 << 2),
	READY = (1 << 3),
	UNKNOWN = (1 << 4),
	SATISFIED = (1 << 5),
	EXTENDED = (1 << 6),
	UNSATISFIED = (1 << 7),
	FAILED = (1 << 8),
	LOOKED = (1 << 9),
	RESET = (1 << 10),
} State;

typedef enum Wrag
{
	PREFIX = 0,
	BEFORE = 1,
	AFTER = 2,
	POSTFIX = 3,
} Wrag;

typedef enum GTag
{
	ANDTAG,
	ITETAG,
	XORTAG
} GTag;

/*------------------------------------------------------------------------*/

typedef int Exp;
typedef uint64_t Mnt;
typedef int64_t Flt;
typedef int64_t Cnf;
typedef uint64_t Fun[FUNQUADS];
typedef signed char Val;
typedef Flt LKHD;

/*------------------------------------------------------------------------*/

typedef struct Conf
{
	int lit, rsn[2];
} Conf;
typedef struct Ctk
{
	struct Ctr *start, *top, *end;
} Ctk;
typedef struct Buf
{
	char* start;
	size_t size, count;
} Buf;
typedef struct DFOPF
{
	int observed, pushed, flag;
} DFOPF;
typedef struct DFPR
{
	int discovered, finished, parent, root;
} DFPR;
typedef struct EVar
{
	int occ[2], pos, score;
} EVar;
typedef struct Ftk
{
	Flt *start, *top, *end;
} Ftk;
typedef struct HTS
{
	unsigned offset;
	unsigned count;
} HTS;
typedef struct Lim
{
	int64_t confs, decs, props;
} Lim;
typedef struct PAGSL
{
	int psm, act, glue, size, lidx;
} PAGSL;
typedef struct PSz
{
	int pos, size;
} PSz;
typedef struct RNG
{
	unsigned z, w;
} RNG;
typedef struct Stk
{
	int *start, *top, *end;
} Stk;
typedef struct Timer
{
	double start;
	int idx, ign;
} Timer;
typedef struct Timers
{
	Timer stk[MAXPHN];
	int nest;
} Timers;
typedef struct Trv
{
	void* state;
	void (*trav)(void*, int);
} Trv;
typedef struct TVar
{
	signed int val : 30;
	unsigned mark : 2;
} TVar;
typedef struct Wtk
{
	struct Work *start, *top, *end;
} Wtk;

/*------------------------------------------------------------------------*/

typedef struct EMA
{
	int shift, count;
	int64_t val;
} EMA;

typedef struct AVG
{
	int64_t val, count;
} AVG;

#ifndef NLGLDEMA

typedef struct DEMA
{
	EMA ema[2];
	int64_t val;
} DEMA;

#define MACDEMA DEMA
#define lglinitmacdema lglinitdema
#define lglupdatemacdema lglupdatedema

#else

#define MACDEMA EMA
#define lglinitmacdema lglinitema
#define lglupdatemacdema(A, B, C) lglupdatema(A, B, C, 1)

#endif

typedef struct MACD
{
	MACDEMA fast, slow;
	struct
	{
		int64_t actual;
		EMA smoothed;
	} diff;
} MACD;

/*------------------------------------------------------------------------*/

typedef struct Ctr
{
	signed int decision : 28;
	unsigned used : 2;
	unsigned used2 : 2;
} Ctr;

typedef struct DVar
{
	HTS hts[2];
} DVar;

#define GLAGBITS 30
#define MAXGLAG ((1 << (GLAGBITS - 1)) - 1)

typedef struct QVar
{
	Flt score;
	unsigned enqueued : 1;
	unsigned important : 1;
	signed int glag : GLAGBITS;
	int pos;
} QVar;

typedef struct TD
{
	signed int level : 30;
	unsigned lrglue : 1, irr : 1;
	int rsn[2];
} TD;

typedef struct AVar
{
	unsigned type : 4;
#ifndef NDEBUG
	unsigned simp : 1, wasfalse : 1;
#endif
	unsigned equiv : 1, lcamark : 4;
	signed int phase : 2, bias : 2, fase : 2;
	unsigned inred : 2, poisoned : 1, assumed : 2, failed : 2;
	unsigned donotelm : 1, donotblk : 1, donoternres : 1;
	unsigned donotbasicprobe : 1, donotsimpleprobe : 1, donotreelook : 1, donotsweep : 1;
#ifndef NLGLYALSAT
	signed int locsval : 2;
#endif
	int mark, trail;
} AVar;

typedef struct Ext
{
	unsigned equiv : 1, melted : 1, blocking : 2, eliminated : 1, tmpfrozen : 1, imported : 1;
	unsigned assumed : 2, failed : 2, aliased : 1, important : 1;
	signed int val : 2, oldval : 2;
	int repr, frozen;
} Ext;

typedef struct Work
{
	unsigned wrag : 2;
	signed int lit : 30, other : 30;
	unsigned red : 1, removed : 1;
} Work;

typedef struct DFL
{
	int discovered, finished;
	union
	{
		int lit, sign;
	};
#ifndef NLGLOG
	int lit4logging;
#endif
} DFL;

/*------------------------------------------------------------------------*/

#define FEATURES                                                                                                       \
	FEATURE(n)                                                                                                         \
	FEATURE(s)                                                                                                         \
	FEATURE(vo)                                                                                                        \
	FEATURE(vc)                                                                                                        \
	FEATURE(co)                                                                                                        \
	FEATURE(cc)                                                                                                        \
	FEATURE(b)                                                                                                         \
	FEATURE(t)                                                                                                         \
	FEATURE(q)                                                                                                         \
	FEATURE(c1)                                                                                                        \
	FEATURE(c2)                                                                                                        \
	FEATURE(c3)                                                                                                        \
	FEATURE(c4)                                                                                                        \
	FEATURE(x)                                                                                                         \
	FEATURE(a1)                                                                                                        \
	FEATURE(a2)                                                                                                        \
	FEATURE(g)                                                                                                         \
	FEATURE(j)                                                                                                         \
	FEATURE(c)                                                                                                         \
	FEATURE(o)

typedef struct Features
{
#define FEATURE(NAME) int NAME;
	FEATURES
} Features;

#define NFEATURES ((int)(sizeof(Features) / sizeof(int)))

#undef FEATURE
#define FEATURE(NAME) #NAME,

static const char* featurenames[] = { FEATURES };

/*------------------------------------------------------------------------*/

typedef struct Stats
{
	int64_t agility;
	int64_t steps, trims, bins, trns, times;
	int defrags, reported, features, repcntdown, gcs, decomps;
	struct
	{
		int clauses, vars;
	} rescored;
	struct
	{
		int64_t count, checked, skipped, forced;
		int64_t agile, delayed, blocked, notforced, reused;
		struct
		{
			int64_t count, pen, delta;
		} delta;
		struct
		{
			int64_t count, sum;
		} kept;
	} restarts;
	struct
	{
		int count, arith, memlim, reset;
		int64_t collected, retired;
	} reduced;
	struct
	{
		int64_t decs, pure, calls, sat, uns, lim, levels;
		struct
		{
			int64_t confs, decs, props;
		} child;
	} prune;
	int64_t prgss, irrprgss, enlwchs, pshwchs, dense, sparse;
	int64_t confs, decisions, hdecs, qdecs, randecs, randphases;
	int64_t uips, decflipped;
	MACD glue, jlevel;
	EMA tlevel;
	AVG avglue;
	struct
	{
		MACD avg;
		int count;
	} its;
	struct
	{
		struct
		{
			int cur, max;
			int64_t add;
		} clauses, lits;
		int64_t maxbytes;
	} irr;
	struct
	{
		int64_t sat, mosat, simp, deref, fixed, freeze, lkhd;
		int64_t melt, add, assume, cassume, failed, repr;
	} calls;
	struct
	{
		int64_t search, simp, lkhd;
	} props, visits;
	struct
	{
		size_t current, max;
	} bytes;
	struct
	{
		int bin, trn, lrg;
	} red;
	struct
	{
		int cnt, simple, trn, lrg, sub;
	} hbr;
	struct
	{
		int current, sum;
	} fixed, equiv;
	struct
	{
		int count, bin, trn;
		int64_t steps;
	} ternres;
	struct
	{
		int count, clauses, lits, pure;
		int64_t res, steps;
	} blk;
	struct
	{
		struct
		{
			int count, failed, eqs;
			int64_t probed, steps;
		} simple;
		struct
		{
			int count, failed, lifted;
			int64_t probed, steps, lastate;
			struct
			{
				int trnr, lrg, count;
			} ate;
		} basic;
		struct
		{
			int count, failed, lifted;
			int64_t probed, steps;
		} treelook;
	} prb;
	struct
	{
		int count, red, failed;
		int64_t lits, bins, steps;
	} trd;
	struct
	{
		int removed, red;
	} bindup;
	struct
	{
		int count, rounds;
		struct
		{
			int trds, failed, sccs;
			int64_t sumsccsizes;
		} stamp;
		struct
		{
			int lits, bin, trn, lrg;
		} failed;
		struct
		{
			int bin, trn, lrg, red;
		} tauts;
		struct
		{
			int bin, trn, lrg;
		} units;
		struct
		{
			int trn, lrg, red;
		} hbrs, str;
		int64_t steps;
	} unhd;
	struct
	{
		int count, elmd, pure, large, sub, str, blkd, rounds;
		struct
		{
			int elm, tried, failed;
		} small;
		int64_t resolutions, copies, subchks, strchks, ipos, steps;
	} elm;
	struct
	{
		int sub2, sub3, subl, str2, str3, str3self, strl, strlself;
		struct
		{
			int64_t lits, clauses, occs;
		} tried;
		int64_t steps;
	} bkwd;
	struct
	{
		struct
		{
			int64_t irr, red, bin, trn, lrg;
		} sub, str;
		int64_t driving, restarting, total;
	} otfs;
	struct
	{
		int64_t nonmin, learned;
	} lits;
	struct
	{
		int64_t learned, glue, realglue, nonmaxglue, scglue;
		struct
		{
			int64_t count, kept;
		} maxglue;
	} clauses;
	struct
	{
		int clauses;
		int64_t added, reduced, retired, resolved, forcing, conflicts;
		int64_t maxbytes;
	} lir[POW2GLUE];
	struct
	{
		int count;
		int64_t set, pos, neg;
	} phase;
	struct
	{
		int count;
		struct
		{
			int confs, irr, vars, its, bin, trn;
		} limhit;
	} simp;
	struct
	{
		int count, gcs, units, equivs, trneqs;
		struct
		{
			struct
			{
				int total, last;
			} max;
			struct
			{
				int64_t total, exactly1;
			} sum;
		} arity;
		struct
		{
			int64_t extr, elim;
		} steps;
		struct
		{
			int64_t total, exactly1, last;
		} extracted;
	} gauss;
	struct
	{
		int count, eliminated, ate, abce, failed, lifted;
		int64_t steps, probed;
		struct
		{
			int64_t search, hits, cols, ins, rsz;
		} cache;
	} cce;
	struct
	{
		int count, units, expam1, resched;
		int64_t steps, eliminated, resolved, subsumed;
		struct
		{
			struct
			{
				struct
				{
					int64_t sum, cnt;
					int max;
				} total;
				struct
				{
					int cnt, max;
				} last;
			} am1, am2;
		} found;
		struct
		{
			struct
			{
				int64_t sum, cnt;
			} am1, am2;
		} used;
	} card;
	struct
	{
		int64_t bin, trn;
	} moved;
	struct
	{
		int count;
		int64_t added, skipped, steps;
	} bca;
	struct
	{
		struct
		{
			int64_t produced;
			struct
			{
				int64_t actual, tried, calls;
			} consumed;
		} cls, units;
	} sync;
	struct
	{
		struct
		{
			int64_t orig, red;
		} sum;
	} deco;
	struct
	{
		int64_t min, bin, size, deco;
		struct
		{
			int64_t search, hits;
		} poison;
		struct
		{
			int64_t search, hits;
		} usedtwice;
	} mincls;
	struct
	{
		struct
		{
			int64_t tried, red, sat;
		} cls, lits;
		struct
		{
			int64_t red, sum;
		} jlevel;
	} redcls;
	int64_t drupped, druplig;
	struct
	{
		int64_t count, tried, cands, sub;
	} subl;
	struct
	{
		int count;
		int64_t flips, mems;
		int min;
	} locs;
	struct
	{
		int count, failed, impls, equivs;
		int64_t rounds, steps, cached, sat, unsat, decs, confs;
		struct
		{
			int64_t classes, envs;
		} sumsize;
		struct
		{
			int64_t total;
			struct
			{
				int64_t count, sat, unsat;
			} type[3];
		} queries;
	} sweep;
	struct
	{
		int64_t count;
		int max, min;
	} setscincf;
	struct
	{
		int64_t tried, locked;
	} promote;
	struct
	{
		int count;
		int64_t quat, self1, self2, dup;
	} quatres;
	struct
	{
		int64_t flushed, sorted;
	} queue;
	struct
	{
		int64_t count, lits;
	} bump;
	struct
	{
		int64_t count;
		EMA avg;
		int changed, level;
	} stability;
} Stats;

/*------------------------------------------------------------------------*/

typedef struct Times
{
	double all, search, preprocessing, inprocessing; // level 0
	double lookahead;								 /// PUT NOTHING HERE!                       // level 0
#define TIMESLEVEL0 lookahead
	double elim, transred, block, unhide, ternres;		// level 1
	double gauss, quatres, card, cce, bca, locs, sweep; // level 1
	double showscoredist, showfeatures, prune;			// level 1
	double probe;										///////PUT NOTHING HERE!                       // level 1
#define TIMESLEVEL1 probe
	struct
	{
		double simple, basic, treelook;
	} prb;									// level 2
	double quatres1, quatres2;				// level 2
	double gc, decompose, queuesort, phase; // level 2
	double backward;						////PUT NOTHING HERE!                       // level 2
#define TIMESLEVEL2 backward
	double restart, defrag; // level 3
	double reduce;			//////PUT NOTHING HERE!                       // level 3
#define TIMESLEVEL3 reduce
	double analysis, decide, bump, mincls, druplig;	  // level 4
	double redcls, queuedecision, heapdecision, subl; // level 4
} Times;

#define TIMESLEVEL0IDX (&(((Times*)0)->TIMESLEVEL0) - &((Times*)0)->all)
#define TIMESLEVEL1IDX (&(((Times*)0)->TIMESLEVEL1) - &((Times*)0)->all)
#define TIMESLEVEL2IDX (&(((Times*)0)->TIMESLEVEL2) - &((Times*)0)->all)
#define TIMESLEVEL3IDX (&(((Times*)0)->TIMESLEVEL3) - &((Times*)0)->all)

/*------------------------------------------------------------------------*/

typedef struct Del
{
	int cur, rem;
} Del;

typedef struct PruneLimit
{
	int64_t lim, inc;
} PruneLimit;

typedef struct Limits
{
	int lkhdpen;
	int64_t randec, randphase, dfg;
	struct
	{
		int64_t visits;
		struct
		{
			int64_t add;
			int start;
		} clauses;
		struct
		{
			int start;
		} vars;
	} inc;
	struct
	{
		struct
		{
			int64_t otfs, confs;
		} vars;
	} rescore;
	struct
	{
		int pen;
		Del del;
		int64_t steps, irrprgss;
	} elm, blk;
	struct
	{
		int pen;
		Del del;
		int64_t steps;
	} trd, unhd, ternres, quatres, cce, card, sweep;
	struct
	{
		int pen;
		Del del;
		struct
		{
			int64_t extr, elim;
		} steps;
	} gauss;
	struct
	{
		int64_t confs;
	} restart;
	struct
	{
		int64_t steps;
		struct
		{
			int pen;
			Del del;
		} simple, basic, treelook;
	} prb;
	struct
	{
		int64_t confs, hard, vars, its, bin, trn;
		int cinc, itinc, binc, tinc;
	} simp;
	struct
	{
		int64_t steps, confs;
	} sync;
	struct
	{
		int64_t steps;
	} term;
	struct
	{
		int64_t fixed;
	} gc;
	struct
	{
		Del del;
		int64_t steps, added;
	} bca;
	struct
	{
		int64_t steps, time;
	} trep;
	struct
	{
		int64_t confs, inc;
		int vars;
	} locs;
	struct
	{
		int redlarge, reset;
	} reduce;
	struct
	{
		int64_t decisions, inc;
	} prune;
} Limits;

/*------------------------------------------------------------------------*/

typedef struct Cbs
{
	struct
	{
		int (*fun)(void*);
		void* state;
		int done;
	} term;
	struct
	{
		struct
		{
			void (*fun)(void*, int);
			void* state;
		} produce, consumed;
		struct
		{
			void (*fun)(void*, int**, int**);
			void* state;
		} consume;
	} units;
	struct
	{
		struct
		{
			void (*fun)(void*, int*, int);
			void* state;
		} produce;
		struct
		{
			void (*fun)(void*, int**, int*);
			void* state;
		} consume;
		struct
		{
			void (*fun)(void*, int);
			void* state;
		} consumed;
	} cls;
	struct
	{
		struct
		{
			int* (*fun)(void*);
			void* state;
		} lock;
		struct
		{
			void (*fun)(void*, int, int);
			void* state;
		} unlock;
	} eqs;
	struct
	{
		void (*lock)(void*);
		void (*unlock)(void*);
		void* state;
	} msglock;
	double (*getime)(void);
	void (*onabort)(void*);
	void* abortstate;
} Cbs;

typedef struct BCA
{
	Stk covered;
} BCA;

typedef struct Dis
{
	struct
	{
		Stk bin, trn;
	} red, irr;
} Dis;

typedef struct Elm
{
	int64_t oldsteps;
	int pivot, negcls, necls, neglidx, round, oldelmd;
	Stk lits, next, clv, csigs, sizes, occs, noccs, mark, m2i;
	struct
	{
		Stk stk;
		int *pos, mt, nvars;
	} touched;
	int bkwdocclim;
} Elm;

typedef struct Card
{
	Stk atmost1, atmost2, cards, elim, *occs, units, expam1;
	char *eliminated, *lit2used, *marked;
	signed char* count;
	int* lit2count;
} Card;

typedef struct FltStr
{
	int current;
	char str[MAXFLTSTR][100];
} FltStr;

typedef struct SPE
{
	signed int count : 31;
	unsigned mark : 1, sum;
} SPE;

typedef struct SPrb
{
	Stk units, impls, eqs, counted, marked;
	SPE* spes;
} SPrb;

typedef struct Gauss
{
	Stk xors, order, *occs;
	signed char* eliminated;
	int garbage, next;
} Gauss;

typedef struct CCE
{
	Stk cla, extend, clauses;
	int *rem, bin, trn;
} CCE;

typedef struct SWP
{
	int partitions, round, query, sat, type;
	Stk partition;
	struct
	{
		int64_t classes, envs;
	} sumsize;
	struct
	{
		int count;
		struct
		{
			int a, b;
		} cached;
		Stk stk;
	} decision;
} SWP;

typedef struct Tlk
{
	Stk stk, seen;
	TVar* tvars;
	LKHD* lkhd;
} Tlk;

typedef struct Mem
{
	void* state;
	lglalloc alloc;
	lglrealloc realloc;
	lgldealloc dealloc;
} Mem;

typedef struct Wchs
{
	Stk stk;
	unsigned start[MAXLDFW];
	int free;
} Wchs;

typedef struct Wrk
{
	Stk queue;
	int count, head, size, posonly, fifo, *pos;
} Wrk;

typedef struct Queue
{
	Stk stk;
	int mt, next, sorted;
} Queue;

/*------------------------------------------------------------------------*/

struct LGL
{
	State state;

	int probing, tid, tids;
	int nvars, szvars, maxext, szext, changed, mt, repcntdown;
	int szdrail, next, next2, flushed, level, alevel, wait, glag;
	int unassigned, lrgluereasons, failed, assumed;

	Flt scinc, scincf, maxscore, minscore;
	int scincinc;

	char cceing, gaussing, bcaing, repforcehead, quatres, notrim;
	char searching, simp, allphaseset, flushphases, occs;
	char unhiding, basicprobing, simpleprobing, treelooking, setuponce;
	char eliminating, donotsched, blocking, ternresing, lkhd, allfrozen;
	char blkall, blkrem, blkrtc, elmall, elmrem, elmrtc, sweeprtc, decomposing;
	char frozen, dense, rmredbintrn, notfullyconnected, forcegc, allowforce;
	char ccertc, touching, sweeping;

	int64_t confatlastit;

	LGL* parent;
	int forked;

	Conf conf;
	RNG rng;

	// the state above this line is copied during 'clone' with 'memcpy'

	Mem* mem;
	Opts* opts;
	Stats* stats;
	Times* times;
	Timers* timers;
	Limits* limits;
	Ext* ext;
	int* i2e;
	int* doms;
	DVar* dvars;
	QVar* qvars;
	AVar* avars;
	Val* vals;
	Flt* jwh;
	TD* drail;
	Stk* red;
	Wchs* wchs;

	Ctk control;
#ifndef NDEBUG
	Stk prevclause;
	int prevglue;
#endif
	Stk clause, eclause, extend, irr, trail, frames, promote;
	Stk eassume, assume, learned;
	Stk dsched;
#ifndef NCHKSOL
	Stk orig;
#endif
	Queue queue;

	union
	{
		Elm* elm;
		SPrb* sprb;
		Tlk* tlk;
		Gauss* gauss;
		CCE* cce;
		BCA* bca;
		Card* card;
		SWP* swp;
	};
	union
	{
		Stk lcaseen, sortstk;
	};
	Stk poisoned, seen, esched, minstk, resolvent;
	struct
	{
		Stk bin, trn;
	} saved;
	EVar* evars;
	Dis* dis;
	Wrk* wrk;
	int* repr;

	char closeapitrace;
	FILE* out;
	FILE* apitrace;
	FILE* trace;
	char* prefix;
	Cbs* cbs;

	LGL* clone;

	FltStr* fltstr;
#if !defined(NLGLDRUPLIG)
	Druplig* druplig;
	int drupligunit;
#endif
};

/*-------------------------------------------------------------------------*/

#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n

static const unsigned lglfloorldtab[256] = {
	// 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
	-1,	   0,	  1,	 1,		2,	   2,	  2,	 2,		3,	   3,	  3,	 3,		3,	   3,	  3,	3,
	LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7)
};

static const uint64_t lglbasevar2funtab[6] = {
	0xaaaaaaaaaaaaaaaaull, 0xccccccccccccccccull, 0xf0f0f0f0f0f0f0f0ull,
	0xff00ff00ff00ff00ull, 0xffff0000ffff0000ull, 0xffffffff00000000ull,
};

/*-------------------------------------------------------------------------*/

static unsigned
lglfloorld(unsigned n)
{
	if (n < (1 << 8))
		return lglfloorldtab[n];
	if (n < (1 << 16))
		return 8 + lglfloorldtab[n >> 8];
	if (n < (1 << 24))
		return 16 + lglfloorldtab[n >> 16];
	return 24 + lglfloorldtab[n >> 24];
}

static int
lglispow2(int n)
{
	assert(0 <= n && n <= INT_MAX);
	return !(n & (n - 1));
}

static int
lglceild(int n)
{
	int res = lglfloorld(n);
	if (!lglispow2(n))
		res++;
	return res;
}

static void
lglchkflt(Flt a)
{
#ifndef NDEBUG
	assert(a >= 0);
	assert(FLTMAX >= (uint64_t)a);
#else
	(void)a;
#endif
}

static Exp
lglexp(Flt a)
{
	Flt tmp = a >> FLTPRC;
	Exp res = tmp;
	assert(0 <= res && res <= EXPMAX);
	res -= EXPZRO;
	return res;
}

static Mnt
lglmnt(Flt a)
{
	Mnt res = a & MNTMAX;
	res |= MNTBIT;
	assert(res <= MNTMAX);
	return res;
}

static Flt
lglflt(Exp e, Mnt m)
{
	Flt res;
	if (!m)
		return FLTMIN;
	if (m < MNTBIT) {
		while (!(m & MNTBIT)) {
			m <<= 1;
			if (e > INT_MIN)
				e--;
			else
				break;
		}
	} else {
		while (m > MNTMAX) {
			m >>= 1;
			if (e > INT_MIN)
				e++;
			else
				break;
		}
	}
	if (e < -EXPZRO)
		return FLTMIN;
	if (e > EXPMAX - EXPZRO)
		return FLTMAX;
	e += EXPZRO;
	assert(0 <= e && e <= EXPMAX);
	assert(m <= MNTMAX);
	assert(m & MNTBIT);
	res = m & ~MNTBIT;
	res |= ((Flt)e) << FLTPRC;
	return res;
}

static Flt
lglrat(unsigned n, unsigned d)
{
	Mnt m;
	Exp e;
	if (!n)
		return FLTMIN;
	if (!d)
		return FLTMAX;
	m = n;
	e = 0;
	while (!(m & (1ull << 63)))
		m <<= 1, e--;
	m /= d;
	return lglflt(e, m);
}

#ifndef NDEBUG
double
lglflt2dbl(Flt a)
{
	return lglmnt(a) * pow(2.0, lglexp(a));
}
#endif

static const char*
lglflt2str(LGL* lgl, Flt a)
{
	double d, e;
	assert(lgl->fltstr);
	if (a == FLTMIN)
		return "0";
	if (a == FLTMAX)
		return "inf";
	d = lglmnt(a);
	d /= 4294967296ll;
	e = lglexp(a);
	e += 32;
	lgl->fltstr->current++;
	if (lgl->fltstr->current == MAXFLTSTR)
		lgl->fltstr->current = 0;
	sprintf(lgl->fltstr->str[lgl->fltstr->current], "%.6fd%+03.0f", d, e);
	return lgl->fltstr->str[lgl->fltstr->current];
}

static Flt
lgladdflt(Flt a, Flt b)
{
	Exp e, f, g;
	Mnt m, n, o;
	lglchkflt(a);
	lglchkflt(b);
	if (a == FLTMAX)
		return FLTMAX;
	if (b == FLTMAX)
		return FLTMAX;
	if (a == FLTMIN)
		return b;
	if (b == FLTMIN)
		return a;
	e = lglexp(a);
	f = lglexp(b);
	if (e < f)
		g = e, e = f, f = g, o = a, a = b, b = o;
	m = lglmnt(a);
	n = lglmnt(b);
	if (e - f < (ptrdiff_t)sizeof(m) * 8)
		m += n >> (e - f);
	return lglflt(e, m);
}

static Flt
lglmulflt(Flt a, Flt b)
{
	Exp e, ea, eb;
	Mnt m, ma, mb;
	lglchkflt(a);
	lglchkflt(b);
	if (a == FLTMAX)
		return FLTMAX;
	if (b == FLTMAX)
		return FLTMAX;
	if (a == FLTMIN)
		return FLTMIN;
	if (b == FLTMIN)
		return FLTMIN;
	ea = lglexp(a);
	eb = lglexp(b);
	if (ea > 0 && eb > 0 && (INT_MAX - ea < eb))
		return FLTMAX;
	e = ea + eb;
	if (e > EXPMAX - EXPZRO - 32)
		return FLTMAX;
	e += 32;
	ma = lglmnt(a);
	mb = lglmnt(b);
	ma >>= 1;
	mb >>= 1;
	m = ma * mb;
	assert(3ull << 62);
	m >>= 30;
	return lglflt(e, m);
}

static Flt
lglshflt(Flt a, int s)
{
	Exp e;
	Mnt m;
	if (a == FLTMAX)
		return FLTMAX;
	if (a == FLTMIN)
		return FLTMIN;
	assert(0 <= s);
	e = lglexp(a);
	if (e < INT_MIN + s)
		return FLTMIN;
	e -= s;
	m = lglmnt(a);
	return lglflt(e, m);
}

static const char*
lglscr2str(LGL* lgl, Flt scr)
{
	return lglflt2str(lgl, scr);
}

static void
lglwrn(LGL* lgl, const char* msg, ...)
{
	va_list ap;
	fprintf(lgl->out, "*** warning in '%s': ", __FILE__);
	va_start(ap, msg);
	vfprintf(lgl->out, msg, ap);
	va_end(ap);
	fputc('\n', lgl->out);
	fflush(lgl->out);
}

static void
lgldie(LGL* lgl, const char* msg, ...)
{
	va_list ap;
	fprintf(lgl->out, "*** internal error in '%s': ", __FILE__);
	va_start(ap, msg);
	vfprintf(lgl->out, msg, ap);
	va_end(ap);
	fputc('\n', lgl->out);
	fflush(lgl->out);
	exit(0);
}

static void
lglabort(LGL* lgl)
{
	if (!lgl)
		exit(1);
	if (lgl->opts && lgl->opts->sleeponabort.val) {
		fprintf(stderr,
				"liblgl.a: Process %d will sleep for %d seconds "
				" before continuing with 'lglabort' procedure.\n",
				getpid(),
				lgl->opts->sleeponabort.val);
		fflush(stderr);
		sleep(lgl->opts->sleeponabort.val);
	}
	if (lgl->cbs && lgl->cbs->onabort)
		lgl->cbs->onabort(lgl->cbs->abortstate);
	if (lgl->opts && lgl->opts->exitonabort.val)
		exit(1);
	abort();
}

static const char*
lglprefix(LGL* lgl)
{
	return lgl && lgl->prefix ? lgl->prefix : "c (LGL HAS NO PREFIX YET) ";
}

static int
lglmsgstart(LGL* lgl, int level)
{
#ifndef NLGLOG
	if (lgl->opts->log.val <= 0)
#endif
		if (lgl->opts->verbose.val < level)
			return 0;
	if (lgl->cbs && lgl->cbs->msglock.lock)
		lgl->cbs->msglock.lock(lgl->cbs->msglock.state);
	fputs(lglprefix(lgl), lgl->out);
	if (lgl->tid >= 0)
		fprintf(lgl->out, "%d ", lgl->tid);
	return 1;
}

static void
lglmsgend(LGL* lgl)
{
	fputc('\n', lgl->out);
	fflush(lgl->out);
	if (lgl->cbs && lgl->cbs->msglock.unlock)
		lgl->cbs->msglock.unlock(lgl->cbs->msglock.state);
}

static void
lglprt(LGL* lgl, int level, const char* msg, ...)
{
	va_list ap;
#ifndef NLGLOG
	if (lgl->opts->log.val <= 0)
#endif
		if (lgl->opts->verbose.val < level)
			return;
	lglmsgstart(lgl, level);
	va_start(ap, msg);
	vfprintf(lgl->out, msg, ap);
	va_end(ap);
	lglmsgend(lgl);
}

#ifndef NLGLOG
static void
lglogstart(LGL* lgl, int level, const char* msg, ...)
{
	va_list ap;
	assert(lgl->opts->log.val >= level);
	if (lgl->cbs && lgl->cbs->msglock.lock)
		lgl->cbs->msglock.lock(lgl->cbs->msglock.state);
	fputs(lglprefix(lgl), lgl->out);
	if (lgl->tid >= 0)
		fprintf(lgl->out, "%d ", lgl->tid);
	fprintf(lgl->out, "LOG%d %d ", level, lgl->level);
	va_start(ap, msg);
	vfprintf(lgl->out, msg, ap);
	va_end(ap);
}

#define lglogend lglmsgend
#endif

/*------------------------------------------------------------------------*/

double
lglprocesstime(void)
{
	struct rusage u;
	double res;
	if (getrusage(RUSAGE_SELF, &u))
		return 0;
	res = u.ru_utime.tv_sec + 1e-6 * u.ru_utime.tv_usec;
	res += u.ru_stime.tv_sec + 1e-6 * u.ru_stime.tv_usec;
	return res;
}

static double
lglgetime(LGL* lgl)
{
	lgl->stats->times++;
	if (lgl->cbs && lgl->cbs->getime)
		return lgl->cbs->getime();
	else
		return lglprocesstime();
}

static int
lglprofidx(LGL* lgl, double* timestatsptr)
{
	int res;
	assert((double*)lgl->times <= timestatsptr);
	assert(timestatsptr < (double*)(sizeof *lgl->times + (char*)lgl->times));
	res = timestatsptr - (double*)lgl->times;
	return res;
}

static int
lglignprofidx(LGL* lgl, int idx)
{
	int res;
	assert(0 <= idx);
	assert((size_t)idx < sizeof(Times) / sizeof(double));
	switch (lgl->opts->profile.val) {
		case 0:
			res = (TIMESLEVEL0IDX < idx);
			break;
		case 1:
			res = (TIMESLEVEL1IDX < idx);
			break;
		case 2:
			res = (TIMESLEVEL2IDX < idx);
			break;
		case 3:
			res = (TIMESLEVEL3IDX < idx);
			break;
		default:
			res = 0;
			break;
	}
	return res;
}

static int
lglignprofptr(LGL* lgl, double* timestatsptr)
{
	return lglignprofidx(lgl, lglprofidx(lgl, timestatsptr));
}

static void
lglstart(LGL* lgl, double* timestatsptr)
{
	Timer* timer;
	int ign, idx;
	idx = lglprofidx(lgl, timestatsptr);
	ign = lglignprofidx(lgl, idx);
	assert(lgl->timers->nest < MAXPHN);
	timer = lgl->timers->stk + lgl->timers->nest++;
	timer->idx = idx;
	if ((timer->ign = ign))
		timer->start = 0;
	else
		timer->start = lglgetime(lgl);
}

void
lglflushtimers(LGL* lgl)
{
	double time = lglgetime(lgl), delta, entered, *ptr;
	Timer* timer;
	int nest;
	for (nest = 0; nest < lgl->timers->nest; nest++) {
		timer = lgl->timers->stk + nest;
		if (timer->ign)
			continue;
		entered = timer->start;
		timer->start = time;
		delta = time - entered;
		if (delta < 0)
			delta = 0;
		ptr = timer->idx + (double*)lgl->times;
		*ptr += delta;
	}
}

double
lglsec(LGL* lgl)
{
	REQINIT();
	lglflushtimers(lgl);
	return lgl->times->all;
}

static void
lglstop(LGL* lgl)
{
	Timer* timer;
	assert(lgl->timers->nest > 0);
	timer = lgl->timers->stk + lgl->timers->nest - 1;
	if (!timer->ign)
		lglflushtimers(lgl);
	lgl->timers->nest--;
	assert(lgl->timers->nest >= 0);
}

/*------------------------------------------------------------------------*/

void
lglsetid(LGL* lgl, int tid, int tids)
{
	REQINITNOTFORKED();
	ABORTIF(tid < 0, "negative id");
	ABORTIF(tid >= tids, "id exceed number of ids");
	lgl->tid = tid;
	lgl->tids = tids;
}

/*------------------------------------------------------------------------*/

static void
lglinc(LGL* lgl, size_t bytes)
{
	lgl->stats->bytes.current += bytes;
	if (lgl->stats->bytes.max < lgl->stats->bytes.current) {
		lgl->stats->bytes.max = lgl->stats->bytes.current;
		LOG(5, "maximum allocated %ld bytes", lgl->stats->bytes.max);
	}
}

static void
lgldec(LGL* lgl, size_t bytes)
{
	assert(lgl->stats->bytes.current >= bytes);
	lgl->stats->bytes.current -= bytes;
}

static void*
lglnew(LGL* lgl, size_t bytes)
{
	void* res;
	if (!bytes)
		return 0;
	if (lgl->mem->alloc)
		res = lgl->mem->alloc(lgl->mem->state, bytes);
	else
		res = malloc(bytes);
	if (!res)
		lgldie(lgl, "out of memory allocating %ld bytes", bytes);
	assert(res);
	LOG(5, "allocating %p with %ld bytes", res, bytes);
	lglinc(lgl, bytes);
	if (res)
		memset(res, 0, bytes);
	return res;
}

static void
lgldel(LGL* lgl, void* ptr, size_t bytes)
{
	if (!ptr) {
		assert(!bytes);
		return;
	}
	lgldec(lgl, bytes);
	LOG(5, "freeing %p with %ld bytes", ptr, bytes);
	if (lgl->mem->dealloc)
		lgl->mem->dealloc(lgl->mem->state, ptr, bytes);
	else
		free(ptr);
}

static void*
lglrsz(LGL* lgl, void* ptr, size_t old, size_t new)
{
	void* res;
	assert(!ptr == !old);
	if (!ptr)
		return lglnew(lgl, new);
	if (!new) {
		lgldel(lgl, ptr, old);
		return 0;
	}
	if (old == new)
		return ptr;
	lgldec(lgl, old);
	if (lgl->mem->realloc)
		res = lgl->mem->realloc(lgl->mem->state, ptr, old, new);
	else
		res = realloc(ptr, new);
	if (!res)
		lgldie(lgl, "out of memory reallocating %ld to %ld bytes", old, new);
	assert(res);
	LOG(5, "reallocating %p to %p from %ld to %ld bytes", ptr, res, old, new);
	lglinc(lgl, new);
	if (new > old)
		memset(res + old, 0, new - old);
	return res;
}

/*------------------------------------------------------------------------*/

static char*
lglstrdup(LGL* lgl, const char* str)
{
	char* res;
	NEW(res, strlen(str) + 1);
	return strcpy(res, str);
}

static void
lgldelstr(LGL* lgl, char* str)
{
	DEL(str, strlen(str) + 1);
}

/*------------------------------------------------------------------------*/

static void
lglinitcbs(LGL* lgl)
{
	if (!lgl->cbs)
		NEW(lgl->cbs, 1);
}

void
lglonabort(LGL* lgl, void* abortstate, void (*onabort)(void*))
{
	REQINITNOTFORKED();
	lglinitcbs(lgl);
	lgl->cbs->abortstate = abortstate;
	lgl->cbs->onabort = onabort;
}

void
lglseterm(LGL* lgl, int (*fun)(void*), void* state)
{
	REQINITNOTFORKED();
	lglinitcbs(lgl);
	lgl->cbs->term.fun = fun;
	lgl->cbs->term.state = state;
}

void
lglsetproduceunit(LGL* lgl, void (*fun)(void*, int), void* state)
{
	REQINITNOTFORKED();
	lglinitcbs(lgl);
	lgl->cbs->units.produce.fun = fun;
	lgl->cbs->units.produce.state = state;
}

void
lglsetconsumeunits(LGL* lgl, void (*fun)(void*, int**, int**), void* state)
{
	REQINITNOTFORKED();
	lglinitcbs(lgl);
	lgl->cbs->units.consume.fun = fun;
	lgl->cbs->units.consume.state = state;
}

void
lglsetconsumedunits(LGL* lgl, void (*fun)(void*, int), void* state)
{
	REQINITNOTFORKED();
	lglinitcbs(lgl);
	lgl->cbs->units.consumed.fun = fun;
	lgl->cbs->units.consumed.state = state;
}

void
lglsetproducecls(LGL* lgl, void (*fun)(void*, int*, int), void* state)
{
	REQINITNOTFORKED();
	lglinitcbs(lgl);
	lgl->cbs->cls.produce.fun = fun;
	lgl->cbs->cls.produce.state = state;
}

void
lglsetconsumecls(LGL* lgl, void (*fun)(void*, int**, int*), void* state)
{
	REQINITNOTFORKED();
	lglinitcbs(lgl);
	lgl->cbs->cls.consume.fun = fun;
	lgl->cbs->cls.consume.state = state;
}

void
lglsetconsumedcls(LGL* lgl, void (*fun)(void*, int), void* state)
{
	REQINITNOTFORKED();
	lglinitcbs(lgl);
	lgl->cbs->cls.consumed.fun = fun;
	lgl->cbs->cls.consumed.state = state;
}

void
lglsetlockeq(LGL* lgl, int* (*fun)(void*), void* state)
{
	REQINITNOTFORKED();
	lglinitcbs(lgl);
	lgl->cbs->eqs.lock.fun = fun;
	lgl->cbs->eqs.lock.state = state;
}

void
lglsetunlockeq(LGL* lgl, void (*fun)(void*, int, int), void* state)
{
	REQINITNOTFORKED();
	lglinitcbs(lgl);
	lgl->cbs->eqs.unlock.fun = fun;
	lgl->cbs->eqs.unlock.state = state;
}

void
lglsetmsglock(LGL* lgl, void (*lock)(void*), void (*unlock)(void*), void* state)
{
	REQINITNOTFORKED();
	lglinitcbs(lgl);
	lgl->cbs->msglock.lock = lock;
	lgl->cbs->msglock.unlock = unlock;
	lgl->cbs->msglock.state = state;
}

void
lglsetime(LGL* lgl, double (*time)(void))
{
	REQINITNOTFORKED();
	lglinitcbs(lgl);
	lgl->cbs->getime = time;
}

/*------------------------------------------------------------------------*/

static int
lglfullstk(Stk* s)
{
	return s->top == s->end;
}
static int
lglmtstk(Stk* s)
{
	return s->top == s->start;
}
static size_t
lglcntstk(Stk* s)
{
	return s->top - s->start;
}
static size_t
lglszstk(Stk* s)
{
	return s->end - s->start;
}

static int
lglpeek(Stk* s, int pos)
{
	assert(0 <= pos && pos < (int)lglszstk(s));
	return s->start[pos];
}

static void
lglpoke(Stk* s, int pos, int val)
{
	assert(0 <= pos && pos <= (int)lglszstk(s));
	s->start[pos] = val;
}

static void
lglenlstk(LGL* lgl, Stk* s)
{
	size_t old_size = lglszstk(s);
	size_t new_size = old_size ? 2 * old_size : 1;
	size_t count = lglcntstk(s);
	RSZ(s->start, old_size, new_size);
	s->top = s->start + count;
	s->end = s->start + new_size;
}

static void
lglrelstk(LGL* lgl, Stk* s)
{
	DEL(s->start, lglszstk(s));
	CLRPTR(s);
}

static void
lglshrstk(LGL* lgl, Stk* s, int new_size)
{
	size_t old_size, count = lglcntstk(s);
	assert(new_size >= 0);
	assert(count <= (unsigned)new_size);
	if (new_size > 0) {
		old_size = lglszstk(s);
		RSZ(s->start, old_size, new_size);
		s->top = s->start + count;
		s->end = s->start + new_size;
	} else
		lglrelstk(lgl, s);
}

static void
lglfitstk(LGL* lgl, Stk* s)
{
	lglshrstk(lgl, s, lglcntstk(s));
}

static void
lglpushstk(LGL* lgl, Stk* s, int elem)
{
	if (lglfullstk(s))
		lglenlstk(lgl, s);
	*s->top++ = elem;
}

static void
lglrmstk(Stk* s, int elem)
{
	int *p, *q;
	for (p = s->start; p < s->top; p++)
		if (*p == elem)
			break;
	assert(p < s->top);
	q = p++;
	while (p < s->top)
		*q++ = *p++;
	s->top = q;
}

static int
lglpopstk(Stk* s)
{
	assert(!lglmtstk(s));
	return *--s->top;
}

static int
lgltopstk(Stk* s)
{
	assert(!lglmtstk(s));
	return s->top[-1];
}

static void
lglrststk(Stk* s, int newsz)
{
	assert(0 <= newsz && (unsigned)newsz <= lglcntstk(s));
	s->top = s->start + newsz;
}

static void
lglclnstk(Stk* s)
{
	lglrststk(s, 0);
}

/*------------------------------------------------------------------------*/

static void
lgltrapi(LGL* lgl, const char* msg, ...)
{
	va_list ap;
	assert(lgl->apitrace);
	va_start(ap, msg);
	vfprintf(lgl->apitrace, msg, ap);
	va_end(ap);
	fputc('\n', lgl->apitrace);
	if (lgl->opts->trapiflush.val)
		fflush(lgl->apitrace);
}

static void
lglopenapitrace(LGL* lgl, const char* name)
{
	FILE* file;
	char* cmd;
	int len;
	len = strlen(name);
	if (len >= 3 && !strcmp(name + len - 3, ".gz")) {
		len += 20;
		NEW(cmd, len);
		sprintf(cmd, "gzip -c > %s", name);
		file = popen(cmd, "w");
		DEL(cmd, len);
		if (file)
			lgl->closeapitrace = 2;
	} else {
		file = fopen(name, "w");
		if (file)
			lgl->closeapitrace = 1;
	}
	if (file)
		lgl->apitrace = file;
	else
		lglwrn(lgl, "can not write API trace to '%s'", name);
	TRAPI("init");
}

void
lglwtrapi(LGL* lgl, FILE* apitrace)
{
	REQUIRE(UNUSED);
	ABORTIF(lgl->apitrace, "can only write one API trace");
	lgl->apitrace = apitrace;
	TRAPI("init");
}

/*------------------------------------------------------------------------*/

static unsigned
lglrand(LGL* lgl)
{
	unsigned res;
	lgl->rng.z = 36969 * (lgl->rng.z & 65535) + (lgl->rng.z >> 16);
	lgl->rng.w = 18000 * (lgl->rng.w & 65535) + (lgl->rng.w >> 16);
	res = (lgl->rng.z << 16) + lgl->rng.w;
	LOG(5, "rng %u", res);
	return res;
}

/*------------------------------------------------------------------------*/

static int
lglmtftk(Ftk* ftk)
{
	return ftk->top == ftk->start;
}

static int
lglfullftk(Ftk* ftk)
{
	return ftk->top == ftk->end;
}

static int
lglsizeftk(Ftk* ftk)
{
	return ftk->end - ftk->start;
}

static int
lglcntftk(Ftk* ftk)
{
	return ftk->top - ftk->start;
}

static void
lglrelftk(LGL* lgl, Ftk* ftk)
{
	DEL(ftk->start, lglsizeftk(ftk));
	memset(ftk, 0, sizeof *ftk);
}

static void
lglenlftk(LGL* lgl, Ftk* ftk)
{
	int oldsize = lglsizeftk(ftk);
	int newsize = oldsize ? 2 * oldsize : 1;
	int count = lglcntftk(ftk);
	RSZ(ftk->start, oldsize, newsize);
	ftk->top = ftk->start + count;
	ftk->end = ftk->start + newsize;
}

static void
lglpushftk(LGL* lgl, Ftk* ftk, Flt f)
{
	if (lglfullftk(ftk))
		lglenlftk(lgl, ftk);
	*ftk->top++ = f;
}

static Flt
lgltopftk(Ftk* ftk)
{
	assert(!lglmtftk(ftk));
	return ftk->top[-1];
}

static Flt
lglpopftk(Ftk* ftk)
{
	assert(!lglmtftk(ftk));
	return *--ftk->top;
}

/*------------------------------------------------------------------------*/

static int
lglfullctk(Ctk* ctk)
{
	return ctk->top == ctk->end;
}

static int
lglsizectk(Ctk* ctk)
{
	return ctk->end - ctk->start;
}

static int
lglcntctk(Ctk* ctk)
{
	return ctk->top - ctk->start;
}

static void
lglrelctk(LGL* lgl, Ctk* ctk)
{
	DEL(ctk->start, lglsizectk(ctk));
	memset(ctk, 0, sizeof *ctk);
}

static void
lglenlctk(LGL* lgl, Ctk* ctk)
{
	int oldsize = lglsizectk(ctk);
	int newsize = oldsize ? 2 * oldsize : 1;
	int count = lglcntctk(ctk);
	RSZ(ctk->start, oldsize, newsize);
	ctk->top = ctk->start + count;
	ctk->end = ctk->start + newsize;
}

static void
lglpushcontrol(LGL* lgl, int decision)
{
	Ctk* ctk = &lgl->control;
	Ctr* ctr;
	if (lglfullctk(ctk))
		lglenlctk(lgl, ctk);
	ctr = ctk->top++;
	ctr->decision = decision;
	ctr->used = 0;
	ctr->used2 = 0;
}

static void
lglpopcontrol(LGL* lgl)
{
	assert(lgl->control.top > lgl->control.start);
	--lgl->control.top;
}

static void
lglrstcontrol(LGL* lgl, int count)
{
	while (lglcntctk(&lgl->control) > count)
		lglpopcontrol(lgl);
}

static Ctr*
lglctr(LGL* lgl, int level)
{
	Ctk* ctk = &lgl->control;
	Ctr* res;
	assert(0 < level && level < lglsizectk(ctk));
	res = ctk->start + level;
	return res;
}

static unsigned
lglevelused(LGL* lgl, int level)
{
	return lglctr(lgl, level)->used;
}

static void
lgluselevel(LGL* lgl, int level)
{
	Ctk* ctk = &lgl->control;
	Ctr* ctr;
	assert(0 < level && level < lglsizectk(ctk));
	ctr = ctk->start + level;
	if (!ctr->used) {
		lglpushstk(lgl, &lgl->frames, level);
		LOG(2, "pulled in decision level %d", level);
		ctr->used = 1;
	} else if (ctr->used == 1) {
		LOG(2, "pulled in decision level %d a second time", level);
		ctr->used = 2;
	}
}

static void
lglunuselevel(LGL* lgl, int level)
{
	Ctk* ctk = &lgl->control;
	Ctr* ctr;
	assert(0 < level);
	if (level >= lglcntctk(ctk))
		return;
	ctr = ctk->start + level;
	assert(ctr->used > 0);
	ctr->used = 0;
}

/*------------------------------------------------------------------------*/

void
lglgetenv(LGL* lgl, Opt* opt, const char* lname)
{
	const char *q, *valstr;
	char uname[40], *p;
	int newval, oldval;
	assert(strlen(lname) + 3 + 1 < sizeof(uname));
	uname[0] = 'L';
	uname[1] = 'G';
	uname[2] = 'L';
	p = uname + 3;
	for (q = lname; *q; q++) {
		assert(p < uname + sizeof uname);
		*p++ = toupper((int)*q);
	}
	assert(p < uname + sizeof uname);
	*p = 0;
	valstr = getenv(uname);
	if (!valstr)
		return;
	oldval = opt->val;
	newval = atoi(valstr);
	if (newval < opt->min)
		newval = opt->min;
	if (newval > opt->max)
		newval = opt->max;
	if (newval == oldval)
		return;
	opt->val = newval;
	TRAPI("option %s %d", lname, newval);
	COVER(lgl->clone);
	if (lgl->clone)
		lglsetopt(lgl->clone, lname, newval);
}

static void
lglchkenv(LGL* lgl)
{
	extern char** environ;
	char *src, *eos, *dst;
	char **p, *s, *d;
	int len;
	for (p = environ; (src = *p); p++) {
		if (src[0] != 'L' || src[1] != 'G' || src[2] != 'L')
			continue;
		for (eos = src; *eos && *eos != '='; eos++)
			;
		len = eos - (src + 3);
		NEW(dst, len + 1);
		d = dst;
		for (s = src + 3; s < eos; s++)
			*d++ = tolower((int)*s);
		*d = 0;
		if (!lglhasopt(lgl, dst) && strcmp(dst, "apitrace") && strcmp(dst, "eak"))
			lglwrn(lgl, "invalid 'LGL...' environment '%s'", src);
		DEL(dst, len + 1);
	}
}

#define SETPLAIN(NAME)                                                                                                 \
	do {                                                                                                               \
		if (val)                                                                                                       \
			lgl->opts->NAME.val = 0;                                                                                   \
		else                                                                                                           \
			lgl->opts->NAME.val = lgl->opts->NAME.dflt;                                                                \
	} while (0)

#define SETDRUP SETPLAIN

static void
lglforcedruplig(LGL* lgl, int val)
{
	SETDRUP(bca);
	SETDRUP(card);
	SETDRUP(gauss);
	SETDRUP(smallve);
	SETDRUP(unhide);
}

static void
lglsetdruplig(LGL* lgl, int val)
{
	lglforcedruplig(lgl, val);
	lglprt(lgl, 1, "[druplig] druplig checking switched %s", val ? "on" : "off");
}

static void
lglsetplain(LGL* lgl, int val)
{
	SETPLAIN(bca);
	SETPLAIN(block);
	SETPLAIN(card);
	SETPLAIN(cce);
	SETPLAIN(decompose);
	SETPLAIN(elim);
	SETPLAIN(gauss);
	SETPLAIN(locs);
	SETPLAIN(probe);
	SETPLAIN(quatres);
	SETPLAIN(sweep);
	SETPLAIN(ternres);
	SETPLAIN(transred);
	SETPLAIN(unhide);
	lglprt(lgl, 1, "[plain] plain solving switched %s", val ? "on" : "off");
}

#define SETWAIT(NAME)                                                                                                  \
	do {                                                                                                               \
		if (val)                                                                                                       \
			lgl->opts->NAME.val = lgl->opts->NAME.dflt;                                                                \
		else                                                                                                           \
			lgl->opts->NAME.val = 0;                                                                                   \
	} while (0)

static void
lglsetwait(LGL* lgl, int val)
{
	SETWAIT(batewait);
	SETWAIT(bcawait);
	SETWAIT(blockwait);
	SETWAIT(ccewait);
	SETWAIT(elmblkwait);
	SETWAIT(gausswait);
	SETWAIT(smallvewait);
	SETWAIT(sweepwait);
	SETWAIT(ternreswait);
	SETWAIT(transredwait);
	SETWAIT(unhidewait);
	lglprt(lgl, 1, "[wait] waiting %s", val ? "enabled" : "disabled");
}

static LGL*
lglnewlgl(void* mem, lglalloc alloc, lglrealloc realloc, lgldealloc dealloc)
{
	LGL* lgl = alloc ? alloc(mem, sizeof *lgl) : malloc(sizeof *lgl);
	ABORTIF(!lgl, "out of memory allocating main solver object");
	CLRPTR(lgl);

	lgl->mem = alloc ? alloc(mem, sizeof *lgl->mem) : malloc(sizeof *lgl->mem);
	ABORTIF(!lgl->mem, "out of memory allocating memory manager object");

	lgl->mem->state = mem;
	lgl->mem->alloc = alloc;
	lgl->mem->realloc = realloc;
	lgl->mem->dealloc = dealloc;

	lgl->opts = alloc ? alloc(mem, sizeof(Opts)) : malloc(sizeof(Opts));
	ABORTIF(!lgl->opts, "out of memory allocating option manager object");
	CLRPTR(lgl->opts);

	lgl->stats = alloc ? alloc(mem, sizeof(Stats)) : malloc(sizeof(Stats));
	ABORTIF(!lgl->stats, "out of memory allocating statistic counters");
	CLRPTR(lgl->stats);

	lglinc(lgl, sizeof *lgl);
	lglinc(lgl, sizeof *lgl->mem);
	lglinc(lgl, sizeof *lgl->opts);
	lglinc(lgl, sizeof *lgl->stats);

	return lgl;
}

static void
lglsetmaxminscore(LGL* lgl)
{
	Flt oldmaxscore = lgl->maxscore, oldminscore = lgl->minscore;
	lgl->maxscore = lglflt(DEFSCOREXP, 1);
	lgl->minscore = lglflt(-DEFSCOREXP, 1);
	if (oldmaxscore != lgl->maxscore)
		lglprt(lgl, 1, "[set-maximum-score] maximum score limit %s", lglscr2str(lgl, lgl->maxscore));
	if (oldminscore != lgl->minscore)
		lglprt(lgl, 1, "[set-minimum-score] minimum score limit %s", lglscr2str(lgl, lgl->minscore));
}

static void
lglsetscincf(LGL* lgl, int scincinc)
{
	if (scincinc == lgl->scincinc)
		return;
	lgl->scincf = lglrat(1000 + scincinc, 1000);
	if (!lgl->stats->setscincf.count++)
		lgl->stats->setscincf.min = lgl->stats->setscincf.max = scincinc;
	else {
		if (scincinc < lgl->stats->setscincf.min)
			lgl->stats->setscincf.min = scincinc;
		if (scincinc > lgl->stats->setscincf.max)
			lgl->stats->setscincf.max = scincinc;
	}
	lglprt(lgl,
		   2,
		   "[set-score-increment-%d] factor %s (%d/1000) after %lld conflicts",
		   lgl->stats->setscincf.count,
		   lglscr2str(lgl, lgl->scincf),
		   scincinc,
		   (LGLL)lgl->stats->confs);
	lgl->scincinc = scincinc;
}

static void
lglinitscores(LGL* lgl)
{
	lglsetmaxminscore(lgl);
	lglsetscincf(lgl, lgl->opts->scincinc.val);
}

LGL*
lglminit(void* mem, lglalloc alloc, lglrealloc realloc, lgldealloc dealloc)
{
	const char* apitracename;
	LGL* lgl;
	int i;

	lgl = 0;
	ABORTIF(!alloc + !realloc + !dealloc != 0 && !alloc + !realloc + !dealloc != 3,
			"inconsistent set of external memory handlers");

	assert(sizeof(long) == sizeof(void*));

	assert(REMOVED > (RMSHFTLIT(MAXVAR) | MASKCS | REDCS));
	assert(REMOVED > MAXREDLIDX);
	assert(REMOVED > MAXIRRLIDX);

	assert(MAXREDLIDX == MAXIRRLIDX);
	assert(GLUESHFT == RMSHFT);

	assert(INT_MAX > ((MAXREDLIDX << GLUESHFT) | GLUEMASK));
	assert(INT_MAX > ((MAXIRRLIDX << RMSHFT) | MASKCS | REDCS));

	assert(MAXGLUE < POW2GLUE);

	lgl = lglnewlgl(mem, alloc, realloc, dealloc);
	lgl->tid = -1;

	lglpushcontrol(lgl, 0);
	assert(lglcntctk(&lgl->control) == lgl->level + 1);

	lgl->out = stdout;
	lgl->prefix = lglstrdup(lgl, "c ");

	apitracename = getenv("LGLAPITRACE");
	if (apitracename)
		lglopenapitrace(lgl, apitracename);

	lglinitopts(lgl, lgl->opts);

	if (lgl->opts->plain.val)
		lglsetplain(lgl, 1);
	if (lgl->opts->druplig.val)
		lglsetdruplig(lgl, 1);
	if (!lgl->opts->wait.val)
		lglsetwait(lgl, 0);

	NEW(lgl->times, 1);
	NEW(lgl->timers, 1);
	NEW(lgl->limits, 1);
	NEW(lgl->fltstr, 1);
	NEW(lgl->red, MAXGLUE + 1);
	NEW(lgl->wchs, 1);
	for (i = 0; i < MAXLDFW; i++)
		lgl->wchs->start[i] = UINT_MAX;
	lglpushstk(lgl, &lgl->wchs->stk, (int)UINT_MAX);
	lglpushstk(lgl, &lgl->wchs->stk, (int)UINT_MAX);

	lgl->scinc = lglflt(0, 1);

	TRANS(UNUSED);

	return lgl;
}

static void
lglcopyclonenfork(LGL* dst, LGL* src)
{
	memcpy(dst->opts, src->opts, sizeof *src->opts);
	dst->out = src->out;
	if (dst->prefix)
		lgldelstr(dst, dst->prefix);
	dst->prefix = lglstrdup(dst, src->prefix);
	if (src->cbs) {
		lglinitcbs(dst);
		if (src->cbs->onabort) {
			dst->cbs->abortstate = src->cbs->abortstate;
			dst->cbs->onabort = src->cbs->onabort;
		}
		if (src->cbs->getime)
			dst->cbs->getime = src->cbs->getime;
	}
}

static void
lglcompact(LGL*);

LGL*
lglmclone(LGL* orig, void* mem, lglalloc alloc, lglrealloc realloc, lgldealloc dealloc)
{
	size_t max_bytes, current_bytes;
	LGL* lgl = orig;
	int glue;

	if (!orig)
		return 0;
	lglcompact(orig);
	LOG(1, "cloning");
	lgl = lglnewlgl(mem, alloc, realloc, dealloc);
	memcpy(lgl, orig, ((char*)&orig->mem) - (char*)orig);
	max_bytes = lgl->stats->bytes.max;
	current_bytes = lgl->stats->bytes.current;
	memcpy(lgl->stats, orig->stats, sizeof *orig->stats);
	lgl->stats->bytes.current = current_bytes;
	lgl->stats->bytes.max = max_bytes;
	lglcopyclonenfork(lgl, orig);

	CLONE(limits, 1);
	CLONE(times, 1);
	CLONE(timers, 1);
	assert(!lgl->timers->nest);
	CLONE(fltstr, 1);
	CLONE(ext, orig->szext);
	CLONE(i2e, orig->szvars);
	CLONE(doms, 2 * orig->szvars);
	CLONE(dvars, orig->szvars);
	CLONE(qvars, orig->szvars);
	CLONE(avars, orig->szvars);
	CLONE(vals, orig->szvars);
	CLONE(jwh, 2 * orig->szvars);
	CLONE(drail, orig->szdrail);

	NEW(lgl->red, MAXGLUE + 1);
	for (glue = 0; glue <= MAXGLUE; glue++)
		CLONESTK(red[glue]);

	NEW(lgl->wchs, 1);
	memcpy(lgl->wchs, orig->wchs, sizeof *orig->wchs);
	CLONESTK(wchs->stk);

	CLONESTK(control);
#ifndef NDEBUG
	CLONESTK(prevclause);
	lgl->prevglue = orig->prevglue;
#endif
	CLONESTK(clause);
	CLONESTK(eclause);
	CLONESTK(extend);
	CLONESTK(irr);
	CLONESTK(trail);
	CLONESTK(frames);
	CLONESTK(promote);
	CLONESTK(eassume);
	CLONESTK(assume);
	CLONESTK(learned);
	CLONESTK(dsched);

	CLONESTK(queue.stk);
	lgl->queue.mt = orig->queue.mt;
	lgl->queue.next = orig->queue.next;

#ifndef NCHKSOL
	CLONESTK(orig);
#endif
#ifndef NDEBUG
	{
		const char* p;
		for (p = (char*)&orig->elm; p < (char*)(&orig->repr + 1); p++)
			assert(!*p);
	}
#endif
	assert(lgl->stats->bytes.current == orig->stats->bytes.current);
	assert(lgl->stats->bytes.max <= orig->stats->bytes.max);
	lgl->stats->bytes.max = orig->stats->bytes.max;
	return lgl;
}

LGL*
lglclone(LGL* lgl)
{
	REQINIT();
	ABORTIF(lgl->opts->druplig.val, "can not clone if Druplig checking is enabled");
	return lglmclone(lgl, lgl->mem->state, lgl->mem->alloc, lgl->mem->realloc, lgl->mem->dealloc);
}

void
lglchkclone(LGL* lgl)
{
	REQINITNOTFORKED();
	if (lgl->clone)
		lglrelease(lgl->clone);
	lgl->clone = lglclone(lgl);
	TRAPI("chkclone");
}

LGL*
lglinit(void)
{
	return lglminit(0, 0, 0, 0);
}

static int
lglmaxoptnamelen(LGL* lgl)
{
	int res = 0, len;
	Opt* o;
	for (o = FIRSTOPT(lgl); o <= LASTOPT(lgl); o++)
		if ((len = strlen(o->lng)) > res)
			res = len;
	return res;
}

void
lglusage(LGL* lgl)
{
	char fmt[20];
	int len;
	Opt* o;
	REQINITNOTFORKED();
	len = lglmaxoptnamelen(lgl);
	sprintf(fmt, "--%%-%ds", len);
	for (o = FIRSTOPT(lgl); o <= LASTOPT(lgl); o++) {
		fprintf(lgl->out, "   ");
		fprintf(lgl->out, fmt, o->lng);
		fprintf(lgl->out, " %s [%d]\n", o->descrp, o->val);
	}
}

static int
lglstrhasprefix(const char* str, const char* prefix)
{
	const char *p, *q;
	for (p = str, q = prefix; *q && *p == *q; p++, q++)
		;
	return !*q;
}

static int
lglignopt(const char* name)
{
	if (!strcmp(name, "abstime"))
		return 1;
	if (!strcmp(name, "check"))
		return 1;
	if (lglstrhasprefix(name, "drup"))
		return 1;
	if (!strcmp(name, "exitonabort"))
		return 1;
	if (!strcmp(name, "log"))
		return 1;
	if (!strcmp(name, "sleeponabort"))
		return 1;
	if (!strcmp(name, "verbose"))
		return 1;
	if (!strcmp(name, "witness"))
		return 1;
	return 0;
}

void
lglopts(LGL* lgl, const char* prefix, int ignsome)
{
	Opt* o;
	REQINITNOTFORKED();
	for (o = FIRSTOPT(lgl); o <= LASTOPT(lgl); o++) {
		if (ignsome && lglignopt(o->lng))
			continue;
		fprintf(lgl->out, "%s--%s=%d\n", prefix, o->lng, o->val);
	}
}

void
lglrgopts(LGL* lgl)
{
	Opt* o;
	REQINITNOTFORKED();
	for (o = FIRSTOPT(lgl); o <= LASTOPT(lgl); o++)
		fprintf(lgl->out, "%s %d %d %d\n", o->lng, o->val, o->min, o->max);
}

void
lglpcs(LGL* lgl, int mixed)
{
	int i, printi, printl;
	int64_t range;
	Opt* o;
	REQINITNOTFORKED();
	for (o = FIRSTOPT(lgl); o <= LASTOPT(lgl); o++) {
		if (lglignopt(o->lng))
			continue;
		range = o->max;
		range -= o->min;
		if (range >= 7 && mixed < 0)
			continue;
		printi = printl = 0;
		printf("%s ", o->lng);
		if (range < 7) {
			printf("{%d", o->min);
			for (i = o->min + 1; i <= o->max; i++)
				printf(",%d", i);
			printf("}");
		} else if (!mixed) {
			printf("[%d,%d]", o->min, o->max);
			printi = 1;
			printl = (o->min > 0 && range >= 100);
		} else if (o->dflt == o->min || o->dflt == o->max) {
			printf("{%d,%d,%d,%d,%d}",
				   o->min,
				   (int)(o->min + (1 * range + 3) / 4),
				   (int)(o->min + (2 * range + 3) / 4),
				   (int)(o->min + (3 * range + 3) / 4),
				   o->max);
		} else if (o->dflt == o->min + 1) {
			printf("{%d,%d,%d,%d}", o->min, o->dflt, (int)(o->dflt + (o->max - (int64_t)o->dflt) / 2), o->max);
		} else if (o->dflt + 1 == o->max) {
			printf("{%d,%d,%d,%d}", o->min, (int)(o->min + (o->dflt - (int64_t)o->min) / 2), o->dflt, o->max);
		} else {
			assert(o->dflt - o->min >= 2);
			assert(o->max - o->dflt >= 2);
			printf("{%d,%d,%d,%d,%d}",
				   o->min,
				   (int)(o->min + (o->dflt - (int64_t)o->min) / 2),
				   o->dflt,
				   (int)(o->dflt + (o->max - (int64_t)o->min) / 2),
				   o->max);
		}
		printf("[%d]", o->dflt);
		if (printi)
			printf("i");
		if (printl)
			printf("l");
		printf(" # %s\n", o->descrp);
	}
}

int
lglhasopt(LGL* lgl, const char* opt)
{
	Opt* o;
	REQINITNOTFORKED();
	for (o = FIRSTOPT(lgl); o <= LASTOPT(lgl); o++)
		if (!strcmp(o->lng, opt))
			return 1;
	return 0;
}

void*
lglfirstopt(LGL* lgl)
{
	return FIRSTOPT(lgl);
}

void*
lglnextopt(LGL* lgl, void* current, const char** nameptr, int* valptr, int* minptr, int* maxptr)
{
	Opt *opt = current, *res = opt + 1;
	if (res > LASTOPT(lgl))
		return 0;
	if (nameptr)
		*nameptr = opt->lng;
	if (valptr)
		*valptr = opt->val;
	if (minptr)
		*minptr = opt->min;
	if (maxptr)
		*maxptr = opt->max;
	return res;
}

void
lglsetopt(LGL* lgl, const char* opt, int val)
{
	int oldval;
	Opt* o;
	REQINITNOTFORKED();
	for (o = FIRSTOPT(lgl); o <= LASTOPT(lgl); o++)
		if (!strcmp(o->lng, opt))
			break;
	if (o > LASTOPT(lgl))
		return;
	if (val < o->min)
		val = o->min;
	if (o->max < val)
		val = o->max;
	oldval = o->val;
	o->val = val;
	if (o == &lgl->opts->plain) {
		if (val > 0 && !oldval)
			lglsetplain(lgl, 1);
		if (!val && oldval)
			lglsetplain(lgl, 0);
	}
	if (o == &lgl->opts->druplig) {
		if (val > 0 && !oldval)
			lglsetdruplig(lgl, 1);
		if (!val && oldval)
			lglsetdruplig(lgl, 0);
	}
	if (o == &lgl->opts->wait) {
		if (val > 0 && !oldval)
			lglsetwait(lgl, 1);
		if (!val && oldval)
			lglsetwait(lgl, 0);
	}
	if (o == &lgl->opts->phase && val != oldval)
		lgl->flushphases = 1;
	if (lgl->state == UNUSED)
		TRANS(OPTSET);
	TRAPI("option %s %d", opt, val);

	if (lgl->opts->druplig.val)
		lglforcedruplig(lgl, 1);
}

static int
lglws(int ch)
{
	return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
}

int
lglreadopts(LGL* lgl, FILE* file)
{
	int res, ch, val, nvalbuf, noptbuf;
	char optbuf[40], valbuf[40];
	const char* opt;
	res = 0;
	for (;;) {
		while (lglws(ch = getc(file)))
			;
		if (ch == EOF)
			break;
		noptbuf = 0;
		optbuf[noptbuf++] = ch;
		while ((ch = getc(file)) != EOF && !lglws(ch)) {
			if (noptbuf + 1 >= (int)sizeof optbuf) {
				ch = EOF;
				break;
			}
			optbuf[noptbuf++] = ch;
		}
		if (ch == EOF)
			break;
		assert(noptbuf < (int)sizeof optbuf);
		optbuf[noptbuf++] = 0;
		assert(lglws(ch));
		while (lglws(ch = getc(file)))
			;
		if (ch == EOF)
			break;
		nvalbuf = 0;
		valbuf[nvalbuf++] = ch;
		while ((ch = getc(file)) != EOF && !lglws(ch)) {
			if (nvalbuf + 1 >= (int)sizeof valbuf)
				break;
			valbuf[nvalbuf++] = ch;
		}
		assert(nvalbuf < (int)sizeof valbuf);
		valbuf[nvalbuf++] = 0;
		opt = optbuf;
		val = atoi(valbuf);
		lglprt(lgl, 1, "read option --%s=%d", opt, val);
		lglsetopt(lgl, opt, val);
		res++;
	}
	return res;
}

void
lglsetout(LGL* lgl, FILE* out)
{
	lgl->out = out;
}

void
lglsetrace(LGL* lgl, FILE* trace)
{
	lgl->trace = trace;
}

FILE*
lglgetout(LGL* lgl)
{
	return lgl->out;
}

void
lglsetprefix(LGL* lgl, const char* prefix)
{
	lgldelstr(lgl, lgl->prefix);
	lgl->prefix = lglstrdup(lgl, prefix);
}

const char*
lglgetprefix(LGL* lgl)
{
	return lgl->prefix;
}

static Opt*
lgligetopt(LGL* lgl, const char* opt)
{
	Opt* o;
	REQINITNOTFORKED();
	for (o = FIRSTOPT(lgl); o <= LASTOPT(lgl); o++)
		if (!strcmp(o->lng, opt))
			return o;
	return 0;
}

int
lglgetopt(LGL* lgl, const char* opt)
{
	Opt* o = lgligetopt(lgl, opt);
	return o ? o->val : 0;
}

int
lgldefopt(LGL* lgl, const char* opt)
{
	Opt* o = lgligetopt(lgl, opt);
	return o ? o->dflt : 0;
}

int
lglgetoptminmax(LGL* lgl, const char* opt, int* min_ptr, int* max_ptr)
{
	Opt* o = lgligetopt(lgl, opt);
	if (!o)
		return 0;
	if (min_ptr)
		*min_ptr = o->min;
	if (max_ptr)
		*max_ptr = o->max;
	return o->val;
}

/*------------------------------------------------------------------------*/

static void
lglrszvars(LGL* lgl, int new_size)
{
	int old_size = lgl->szvars;
	assert(lgl->nvars <= new_size);
	RSZ(lgl->vals, old_size, new_size);
	RSZ(lgl->i2e, old_size, new_size);
	RSZ(lgl->doms, 2 * old_size, 2 * new_size);
	RSZ(lgl->dvars, old_size, new_size);
	RSZ(lgl->avars, old_size, new_size);
	RSZ(lgl->qvars, old_size, new_size);
	RSZ(lgl->jwh, 2 * old_size, 2 * new_size);
	lgl->szvars = new_size;
}

static void
lglenlvars(LGL* lgl)
{
	size_t old_size, new_size;
	old_size = lgl->szvars;
	new_size = old_size ? 2 * old_size : 4;
	LOG(3, "enlarging internal variables from %ld to %ld", old_size, new_size);
	lglrszvars(lgl, new_size);
}

static void
lglredvars(LGL* lgl)
{
	size_t old_size, new_size;
	old_size = lgl->szvars;
	new_size = lgl->nvars;
	if (new_size == old_size)
		return;
	LOG(3, "reducing variables from %ld to %ld", old_size, new_size);
	lglrszvars(lgl, new_size);
}

static int
lglmax(int a, int b)
{
	return a > b ? a : b;
}

/*------------------------------------------------------------------------*/

static DVar*
lgldvar(LGL* lgl, int lit)
{
	assert(2 <= abs(lit) && abs(lit) < lgl->nvars);
	return lgl->dvars + abs(lit);
}

/*------------------------------------------------------------------------*/

static AVar*
lglavar(LGL* lgl, int lit)
{
	assert(2 <= abs(lit) && abs(lit) < lgl->nvars);
	return lgl->avars + abs(lit);
}

static Val
lglval(LGL* lgl, int lit)
{
	int idx = abs(lit);
	Val res;
	ASSERT(2 <= idx && idx < lgl->nvars);
	res = lgl->vals[idx];
	if (lit < 0)
		res = -res;
	return res;
}

static int
lgltrail(LGL* lgl, int lit)
{
	return lglavar(lgl, lit)->trail;
}

static int
lglevel2trail(LGL* lgl, int level)
{
	return lgltrail(lgl, lglctr(lgl, level)->decision);
}

static TD*
lgltd(LGL* lgl, int lit)
{
	int pos = lgltrail(lgl, lit);
	assert(0 <= pos && pos < lgl->szdrail);
	return lgl->drail + pos;
}

static int
lglevel(LGL* lgl, int lit)
{
	return lgltd(lgl, lit)->level;
}

static int
lglisfree(LGL* lgl, int lit)
{
	return lglavar(lgl, lit)->type == FREEVAR;
}

/*------------------------------------------------------------------------*/

static QVar*
lglqvar(LGL* lgl, int lit)
{
	assert(2 <= abs(lit) && abs(lit) < lgl->nvars);
	return lgl->qvars + abs(lit);
}

static int*
lgldpos(LGL* lgl, int lit)
{
	QVar* qv;
	int* res;
	qv = lglqvar(lgl, lit);
	res = &qv->pos;
	return res;
}

static int
lglscrcmp(LGL* lgl, Flt a, Flt b)
{
	const int shift = lgl->opts->scoreshift.val;
	a >>= shift;
	assert(a >= 0);
	b >>= shift;
	assert(b >= 0);
	if (a < b)
		return -1;
	if (a > b)
		return 1;
	return 0;
}

static int
lgldcmp(LGL* lgl, int l, int k)
{
	QVar* pv = lglqvar(lgl, l);
	QVar* qv = lglqvar(lgl, k);
	int res;
	if (!pv->important && qv->important)
		return -1;
	if (pv->important && !qv->important)
		return 1;
	if ((res = lglscrcmp(lgl, pv->score, qv->score)))
		return res;
	return 0;
}

#ifndef NDEBUG
static void
lglchkdsched(LGL* lgl)
{
	int *p, parent, child, ppos, cpos, size, i, tmp;
	Stk* s = &lgl->dsched;
	size = lglcntstk(s);
	p = s->start;
	for (ppos = 0; ppos < size; ppos++) {
		parent = p[ppos];
		assert(parent > 1);
		tmp = *lgldpos(lgl, parent);
		assert(ppos == tmp);
		for (i = 0; i <= 1; i++) {
			cpos = 2 * ppos + 1 + i;
			if (cpos >= size)
				continue;
			child = p[cpos];
			assert(child > 1);
			tmp = *lgldpos(lgl, child);
			assert(cpos == tmp);
			assert(lgldcmp(lgl, parent, child) >= 0);
		}
	}
}
#endif

static void
lgldup(LGL* lgl, int lit)
{
	int child = lit, parent, cpos, ppos, *p, *cposptr, *pposptr;
	Stk* s = &lgl->dsched;
	assert(lit > 1);
	p = s->start;
	cposptr = lgldpos(lgl, child);
	cpos = *cposptr;
	assert(cpos >= 0);
	while (cpos > 0) {
		ppos = (cpos - 1) / 2;
		parent = p[ppos];
		if (lgldcmp(lgl, parent, lit) >= 0)
			break;
		pposptr = lgldpos(lgl, parent);
		assert(*pposptr == ppos);
		p[cpos] = parent;
		*pposptr = cpos;
		LOGDSCHED(5, parent, "down from %d", ppos);
		cpos = ppos;
	}
	if (*cposptr == cpos)
		return;
#ifndef NLGLOG
	ppos = *cposptr;
#endif
	*cposptr = cpos;
	p[cpos] = lit;
	LOGDSCHED(5, lit, "up from %d", ppos);
#ifndef NDEBUG
	if (lgl->opts->check.val >= 2)
		lglchkdsched(lgl);
#endif
}

static void
lglddown(LGL* lgl, int lit)
{
	int parent = lit, child, right, ppos, cpos;
	int *p, *pposptr, *cposptr, size;
	Stk* s = &lgl->dsched;
	assert(lit > 1);
	size = lglcntstk(s);
	p = s->start;
	pposptr = lgldpos(lgl, parent);
	ppos = *pposptr;
	assert(0 <= ppos);
	for (;;) {
		cpos = 2 * ppos + 1;
		if (cpos >= size)
			break;
		child = p[cpos];
		if (cpos + 1 < size) {
			right = p[cpos + 1];
			if (lgldcmp(lgl, child, right) < 0)
				cpos++, child = right;
		}
		if (lgldcmp(lgl, child, lit) <= 0)
			break;
		cposptr = lgldpos(lgl, child);
		assert(*cposptr == cpos);
		p[ppos] = child;
		*cposptr = ppos;
		LOGDSCHED(5, child, "up from %d", cpos);
		ppos = cpos;
	}
	if (*pposptr == ppos)
		return;
#ifndef NLGLOG
	cpos = *pposptr;
#endif
	*pposptr = ppos;
	p[ppos] = lit;
	LOGDSCHED(5, lit, "down from %d", cpos);
#ifndef NDEBUG
	if (lgl->opts->check.val >= 2)
		lglchkdsched(lgl);
#endif
}

static void
lgldsched(LGL* lgl, int lit)
{
	int* p = lgldpos(lgl, lit);
	Stk* s = &lgl->dsched;
	assert(lit > 1);
	assert(!lglqvar(lgl, lit)->enqueued);
	assert(*p < 0);
	*p = lglcntstk(s);
	lglpushstk(lgl, s, lit);
	lgldup(lgl, lit);
	lglddown(lgl, lit);
	LOGDSCHED(4, lit, "pushed");
}

static int
lgltopdsched(LGL* lgl)
{
	assert(!lglmtstk(&lgl->dsched));
	return lgl->dsched.start[0];
}

static int
lglpopdsched(LGL* lgl)
{
	Stk* s = &lgl->dsched;
	int res, last, cnt, *p;
	QVar* qv;
	assert(!lglmtstk(s));
	res = *s->start;
	LOGDSCHED(4, res, "popped");
	qv = lglqvar(lgl, res);
	assert(!qv->pos);
	qv->pos = -1;
	last = lglpopstk(s);
	cnt = lglcntstk(s);
	if (!cnt) {
		assert(last == res);
		return res;
	}
	p = lgldpos(lgl, last);
	assert(*p == cnt);
	*p = 0;
	*s->start = last;
	lglddown(lgl, last);
	return res;
}

#if 0
static void lgldunsched (LGL * lgl, int lit) {
  Stk * s = &lgl->dsched;
  int last, cnt, * p, pos;
  QVar * qv;
  if (lit < 0) lit = -lit;
  assert (!lglmtstk (s));
  LOGDSCHED (4, lit, "unschedule");
  qv = lglqvar (lgl, lit);
  pos = qv->pos;
  qv->pos = -1;
  assert (lglpeek (s, pos) == lit);
  last = lglpopstk (s);
  cnt = lglcntstk (s);
  if (cnt == pos) { assert (last == lit); return; }
  p = lgldpos (lgl, last);
  assert (*p == cnt);
  lglpoke (s, pos, last);
  *p = pos;
  lgldup (lgl, last);
  lglddown (lgl, last);
}
#endif

static void
lgldreschedule(LGL* lgl)
{
	Stk* s = &lgl->dsched;
	int idx, i, pos, cnt = lglcntstk(s);
	QVar* qv;
	LOG(1, "rescheduling %d variables", cnt);
	for (idx = 2; idx < lgl->nvars; idx++) {
		qv = lglqvar(lgl, idx);
		if (qv->enqueued)
			continue;
		qv->pos = -1;
	}
	pos = 0;
	s->top = s->start;
	for (i = 0; i < cnt; i++) {
		assert(pos <= i);
		assert(s->start + pos == s->top);
		idx = s->start[i];
		if (abs(idx) <= 1)
			continue;
		qv = lglqvar(lgl, idx);
		assert(!qv->enqueued);
		if (!lglisfree(lgl, idx)) {
			qv->pos = -1;
			continue;
		}
		assert(qv->pos == -1);
		s->start[pos] = idx;
		qv->pos = pos++;
		s->top++;
		lgldup(lgl, idx);
		lglddown(lgl, idx);
	}
	LOG(1, "new schedule with %d variables", lglcntstk(s));
	lglfitstk(lgl, s);
#ifndef NDEBUG
	if (lgl->opts->check.val >= 1)
		lglchkdsched(lgl);
#endif
}

static void
lglenq(LGL* lgl, int idx, int updatequeuenext)
{
	QVar* qv = lglqvar(lgl, idx);
	assert(!qv->enqueued);
	assert(qv->pos < 0);
	qv->enqueued = 1;
	qv->pos = lglcntstk(&lgl->queue.stk);
	lglpushstk(lgl, &lgl->queue.stk, idx);
	if (updatequeuenext || lgl->queue.next < 0)
		lgl->queue.next = qv->pos;
}
static void
lglrescorevars(LGL* lgl)
{
	Flt oldscinc, oldscore, newscore, oldmaxscore = 0, newmaxscore = 0;
	int64_t newotfs;
	QVar* qv;
	int idx;
	lgl->stats->rescored.vars++;
	for (idx = 2; idx < lgl->nvars; idx++) {
		qv = lglqvar(lgl, idx);
		if (qv->enqueued)
			continue;
		oldscore = qv->score;
		if (oldscore > oldmaxscore)
			oldmaxscore = oldscore;
		if (oldscore == FLTMIN)
			newscore = oldscore;
		else if (oldscore <= lgl->minscore)
			newscore = lgl->minscore;
		else {
			newscore = lglshflt(oldscore, DEFSCOREXP);
			if (newscore < lgl->minscore)
				newscore = lgl->minscore;
		}
		if (newscore <= lgl->minscore) {
			assert(newscore == FLTMIN || newscore == lgl->minscore);
			assert(!qv->enqueued);
		} else
			newscore = (oldscore >> 32);
		qv->score = newscore;
		if (qv->score > newmaxscore)
			newmaxscore = qv->score;
		LOG(3, "rescored variable %d from %s to %s", idx, lglscr2str(lgl, oldscore), lglscr2str(lgl, qv->score));
	}
	lgldreschedule(lgl);
	newotfs = lgl->stats->otfs.driving + lgl->stats->otfs.restarting;
	assert(newotfs >= lgl->limits->rescore.vars.otfs);
	assert(lgl->stats->confs >= lgl->limits->rescore.vars.confs);
	lglprt(lgl,
		   3,
		   "[rescored-vars-%d] after %lld conflicts and %lld OTFS",
		   lgl->stats->rescored.vars,
		   lgl->stats->confs - lgl->limits->rescore.vars.confs,
		   newotfs - lgl->limits->rescore.vars.otfs);
	lgl->limits->rescore.vars.confs = lgl->stats->confs;
	lgl->limits->rescore.vars.otfs = newotfs;
	lglprt(lgl, 3, "[rescored-vars-%d] old maximum score %s", lgl->stats->rescored.vars, lglscr2str(lgl, oldmaxscore));
	lglprt(lgl, 3, "[rescored-vars-%d] new maximum score %s", lgl->stats->rescored.vars, lglscr2str(lgl, newmaxscore));
	oldscinc = lgl->scinc;
	lgl->scinc = lglshflt(oldscinc, DEFSCOREXP);
	assert(lgl->scinc > 0);
	lglprt(lgl, 3, "[rescored-vars-%d] old score increment %s", lgl->stats->rescored.vars, lglscr2str(lgl, oldscinc));
	lglprt(lgl, 3, "[rescored-vars-%d] new score increment %s", lgl->stats->rescored.vars, lglscr2str(lgl, lgl->scinc));
}

static void
lglbumpscinc(LGL* lgl)
{
	Flt oldscinc;
	if (lgl->simp && !lgl->opts->bumpsimp.val)
		return;
	oldscinc = lgl->scinc;
	lgl->scinc = lglmulflt(oldscinc, lgl->scincf);
	LOG(3, "bumped variable score increment from %s to %s", lglscr2str(lgl, oldscinc), lglscr2str(lgl, lgl->scinc));
	if (lgl->scinc >= lgl->maxscore) {
		LOG(2, "variable max score %s hit", lglscr2str(lgl, lgl->maxscore));
		lglrescorevars(lgl);
	}
	if (!lgl->simp && !(lgl->stats->confs % lgl->opts->scincincincint.val)) {
		int newscincinc = lgl->scincinc;
		if (lgl->opts->scincincmode.val == 1)
			newscincinc -= lgl->opts->scincincdelta.val;
		else if (lgl->opts->scincincmode.val == 2)
			newscincinc = (lgl->opts->scincincmin.val + newscincinc) / 2;
		if (newscincinc < lgl->opts->scincincmin.val)
			newscincinc = lgl->opts->scincincmin.val;
		if (newscincinc != lgl->scincinc)
			lglsetscincf(lgl, newscincinc);
	}
}

/*------------------------------------------------------------------------*/

static int
lglnewvar(LGL* lgl)
{
	AVar* av;
	DVar* dv;
	QVar* qv;
	int res;
	assert(!lgl->dense);
	if (lgl->nvars == lgl->szvars)
		lglenlvars(lgl);
	if (lgl->nvars)
		res = lgl->nvars++;
	else
		res = 2, lgl->nvars = 3;
	assert(res < lgl->szvars);
	if (res > MAXVAR)
		lgldie(lgl, "more than %d variables", MAXVAR - 1);
	assert(res <= MAXVAR);
	assert((RMSHFTLIT(res) >> RMSHFT) == res);
	assert((RMSHFTLIT(-res) >> RMSHFT) == -res);
	LOG(3, "new internal variable %d", res);
	dv = lgl->dvars + res;
	CLRPTR(dv);
	av = lgl->avars + res;
	CLRPTR(av);
	qv = lgl->qvars + res;
	CLRPTR(qv);
	qv->glag = -1;
	qv->pos = -1;
	lglenq(lgl, res, 1);
	lgl->unassigned++;
	lgl->allphaseset = 0;
	return res;
}

static int
lglsgn(int lit)
{
	return (lit < 0) ? -1 : 1;
}

static Ext*
lglelit2ext(LGL* lgl, int elit)
{
	int idx = abs(elit);
	assert(0 < idx), assert(idx <= lgl->maxext);
	return lgl->ext + idx;
}

static int
lglerepr(LGL* lgl, int elit)
{
	int res, next, tmp;
	Ext* ext;
	assert(0 < abs(elit)), assert(abs(elit) <= lgl->maxext);
	res = elit;
	for (;;) {
		ext = lglelit2ext(lgl, res);
		if (!ext->equiv)
			break;
		next = ext->repr;
		if (res < 0)
			next = -next;
		res = next;
	}
	tmp = elit;
	for (;;) {
		ext = lglelit2ext(lgl, tmp);
		if (!ext->equiv) {
			assert(tmp == res);
			break;
		}
		next = ext->repr;
		ext->repr = (tmp < 0) ? -res : res;
		if (tmp < 0)
			next = -next;
		tmp = next;
	}
	assert(res);
	return res;
}

static void
lgladjext(LGL* lgl, int eidx)
{
	size_t old, new;
	assert(eidx >= lgl->szext);
	assert(eidx > lgl->maxext);
	assert(lgl->szext >= lgl->maxext);
	old = lgl->szext;
	new = old ? 2 * old : 2;
	while ((unsigned)eidx >= new)
		new *= 2;
	assert((unsigned)eidx < new), assert(new >= (size_t)lgl->szext);
	LOG(3, "enlarging external variables from %ld to %ld", old, new);
	RSZ(lgl->ext, old, new);
	lgl->szext = new;
}

static void
lglmelter(LGL* lgl)
{
	if (lgl->allfrozen) {
		lglprt(lgl, 1, "[melter] not all literals assumed to be frozen anymore");
		lgl->allfrozen = 0;
	}
	if (lgl->limits->elm.pen || lgl->limits->blk.pen || lgl->limits->cce.pen) {
		lglprt(lgl,
			   1,
			   "[melter] reset penalties: %d elm, %d blk, %d cce",
			   lgl->limits->elm.pen,
			   lgl->limits->blk.pen,
			   lgl->limits->cce.pen);
		lgl->limits->elm.pen = lgl->limits->blk.pen = lgl->limits->cce.pen = 0;
	}
	lgl->frozen = lgl->allfrozen = 0;
	LOG(2, "melted solver");
}

static int
lglimportaux(LGL* lgl, int elit)
{
	int res, repr, eidx = abs(elit);
	Ext* ext;
	assert(elit);
	if (eidx >= lgl->szext)
		lgladjext(lgl, eidx);
	if (eidx > lgl->maxext) {
		lgl->maxext = eidx;
		lglmelter(lgl);
	}
	repr = lglerepr(lgl, elit);
	ext = lglelit2ext(lgl, repr);
	assert(!ext->equiv);
	res = ext->repr;
	if (!ext->imported) {
		res = lglnewvar(lgl);
		assert(!ext->equiv);
		ext->repr = res;
		ext->imported = 1;
		assert(eidx <= INT_MAX / 2);
		lgl->i2e[res] = 2 * eidx;
		LOG(3, "mapping external variable %d to %d", eidx, res);
		lglmelter(lgl);
	}
	if (repr < 0)
		res = -res;
	LOG(2, "importing %d as %d", elit, res);
	return res;
}

static int
lglimport(LGL* lgl, int elit)
{
	assert(elit);
	if (!lgl->opts->import.val) {
		if (!lgl->maxext)
			(void)lglimportaux(lgl, 1);
		while (lgl->maxext < abs(elit))
			(void)lglimportaux(lgl, lgl->maxext + 1);
	}
	return lglimportaux(lgl, elit);
}

static Stk*
lglidx2stk(LGL* lgl, int red, int lidx)
{
	int glue = 0;
	Stk* s;
	assert(red == 0 || red == REDCS);
	assert(0 <= lidx);
	if (!red)
		s = &lgl->irr;
	else {
		glue = lidx & GLUEMASK;
		lidx >>= GLUESHFT;
		assert(lidx <= MAXREDLIDX);
		s = &lgl->red[glue];
	}
	return s;
}

static int*
lglidx2lits(LGL* lgl, int red, int lidx)
{
	Stk* s = lglidx2stk(lgl, red, lidx);
	int* res;
	assert(red == 0 || red == REDCS);
	assert(0 <= lidx);
	res = s->start + (red ? (lidx >> GLUESHFT) : lidx);
#ifndef NDEBUG
	if (red && (lidx & GLUEMASK) == MAXGLUE)
		assert(res < s->end);
	else
		assert(res < s->top);
#endif
	return res;
}

#ifndef NLGLOG
static const char*
lglred2str(int red)
{
	assert(!red || red == REDCS);
	return red ? "redundant" : "irredundant";
}
#endif

static int
lgliselim(LGL* lgl, int lit)
{
	Tag tag = lglavar(lgl, lit)->type;
	return tag == ELIMVAR;
}

static int
lglexport(LGL* lgl, int ilit)
{
	int iidx, tidx, eidx, def, res;
	iidx = abs(ilit);
	assert(2 <= iidx), assert(iidx < lgl->nvars);
	tidx = lgl->i2e[iidx];
	def = tidx & 1;
	tidx >>= 1;
	assert(tidx);
	eidx = tidx;
	if (def)
		eidx += lgl->maxext;
	res = eidx;
	if (ilit < 0)
		res = -res;
	return res;
}

static int*
lglrsn(LGL* lgl, int lit)
{
	return lgltd(lgl, lit)->rsn;
}

static int
lglulit(int lit)
{
	return 2 * abs(lit) + (lit < 0);
}

static void
lglsetdom(LGL* lgl, int lit, int dom)
{
	assert(2 <= abs(lit) && abs(lit) < lgl->nvars);
	assert(2 <= abs(dom) && abs(dom) < lgl->nvars);
	ASSERT(lglval(lgl, lit) >= 0);
	ASSERT(lglval(lgl, dom) >= 0);
	lgl->doms[lglulit(lit)] = dom;
	LOG(3, "literal %d dominated by %d", lit, dom);
}

static int
lglgetdom(LGL* lgl, int lit)
{
	int res;
	assert(2 <= abs(lit) && abs(lit) < lgl->nvars);
	assert(lglval(lgl, lit) >= 0);
	res = lgl->doms[lglulit(lit)];
	return res;
}

static HTS*
lglhts(LGL* lgl, int lit)
{
	return lgldvar(lgl, lit)->hts + (lit < 0);
}

static int*
lglhts2wchs(LGL* lgl, HTS* hts)
{
	int* res = lgl->wchs->stk.start + hts->offset;
	assert(res < lgl->wchs->stk.top);
	assert(res + hts->count < lgl->wchs->stk.top);
	assert(res + hts->count < lgl->wchs->stk.top);
	return res;
}

/*------------------------------------------------------------------------*/

#ifndef NLGLDRUPLIG

static int
lgldruplig(LGL* lgl)
{
	if (!lgl->opts->druplig.val)
		return 0;
	if (!lgl->druplig) {
		lgl->druplig = druplig_minit(lgl, (druplig_malloc)lglnew, (druplig_realloc)lglrsz, (druplig_free)lgldel);
		druplig_set_check(lgl->druplig, lgl->opts->drupligcheck.val);
		if (!lgl->opts->drupligtrace.val)
			druplig_set_trace(lgl->druplig, 0);
		else if (lgl->trace)
			druplig_set_trace(lgl->druplig, lgl->trace);
		else
			druplig_set_trace(lgl->druplig, lgl->out);
		druplig_set_traceorig(lgl->druplig, lgl->opts->drupligtraceorig.val);
		if (lgl->opts->verbose.val > 0) {
			druplig_banner(lgl->out);
			druplig_options(lgl->druplig, lgl->out);
		}
	}
	return 1;
}

static void
lgldrupligreallyadd(LGL* lgl, int red)
{
	assert(!red || red == REDCS);
	assert(lgl->opts->druplig.val);
	assert(lgl->druplig);
	lglstart(lgl, &lgl->times->druplig);
	if (red)
		druplig_check_and_add_redundant_clause(lgl->druplig);
	else
		druplig_add_original_clause(lgl->druplig);
	lglstop(lgl);
}

static void
lgldrupligreallydel(LGL* lgl)
{
	assert(lgl->opts->druplig.val);
	assert(lgl->druplig);
	lglstart(lgl, &lgl->times->druplig);
	druplig_forget_clause(lgl->druplig);
	lglstop(lgl);
}

static int
lglialiased(LGL* lgl, int ilit)
{
	int elit = lglexport(lgl, ilit);
	Ext* ext = lglelit2ext(lgl, elit);
	return ext->aliased;
}

#endif

static void
lgldrupligaddclsaux(LGL* lgl, int red, const int* c)
{
#ifndef NLGLDRUPLIG
	assert(!red || red == REDCS);
	if (lgldruplig(lgl)) {
		const int* p;
		int lit;
		for (p = c; (lit = *p); p++)
			druplig_add_literal(lgl->druplig, lglexport(lgl, lit));
		lgldrupligreallyadd(lgl, red);
	}
#else
	(void)lgl, (void)red, (void)c;
#endif
}

static void
lgldrupligaddclsarg(LGL* lgl, int red, ...)
{
#ifndef NLGLDRUPLIG
	assert(!red || red == REDCS);
	if (lgldruplig(lgl)) {
		va_list ap;
		int unit = 0;
#ifndef NLGLOG
		int logging = (lgl->opts->log.val > 1);
		if (logging)
			lglogstart(lgl, 2, "druplig adding internal clause");
#endif
		va_start(ap, red);
		for (;;) {
			int lit = va_arg(ap, int);
			if (!lit)
				break;
			if (unit)
				unit = INT_MAX;
			else
				unit = lit;
			druplig_add_literal(lgl->druplig, lglexport(lgl, lit));
#ifndef NLGLOG
			if (logging)
				fprintf(lgl->out, " %d", lit);
#endif
		}
		va_end(ap);
#ifndef NLGLOG
		if (logging)
			lglogend(lgl);
#endif
		lgldrupligreallyadd(lgl, red);
		if (unit && unit != INT_MAX)
			lgl->drupligunit = unit;
		else
			lgl->drupligunit = 0;
	}
#else
	(void)lgl, (void)red;
#endif
}

static void
lgldrupligaddcls(LGL* lgl, int red)
{
#ifndef NLGLDRUPLIG
	assert(!red || red == REDCS);
	if (lgldruplig(lgl)) {
		int lit, size = 0;
		const int* p;
		for (p = lgl->clause.start; p < lgl->clause.top; p++)
			if ((lit = *p)) {
				druplig_add_literal(lgl->druplig, lglexport(lgl, lit));
				size++;
			}
		lgldrupligreallyadd(lgl, red);
		if (size == 1)
			lgl->drupligunit = lgl->clause.start[0];
		else
			lgl->drupligunit = 0;
	}
#else
	(void)lgl, (void)red;
#endif
}

static void
lgldrupligdelclsarg(LGL* lgl, ...)
{
#ifndef NLGLDRUPLIG
	if (lgldruplig(lgl)) {
		int aliased = 0;
		va_list ap;
#ifndef NDEBUG
		int size = 0;
#endif
#ifndef NLGLOG
		int logging = (lgl->opts->log.val > 1);
		if (logging)
			lglogstart(lgl, 2, "druplig forgetting internal clause");
#endif
		va_start(ap, lgl);
		for (;;) {
			int lit = va_arg(ap, int);
			if (!lit)
				break;
			if (lglialiased(lgl, lit))
				aliased = 1;
#ifndef NDEBUG
			size++;
#endif
#ifndef NLGLOG
			if (logging)
				fprintf(lgl->out, " %d", lit);
#endif
		}
		va_end(ap);
#ifndef NLGLOG
		if (logging) {
			if (aliased)
				fprintf(lgl->out, " (but aliased)");
			lglogend(lgl);
		}
#endif
		assert(size > 1);
		if (aliased)
			return;
		va_start(ap, lgl);
		for (;;) {
			int lit = va_arg(ap, int);
			if (!lit)
				break;
			druplig_add_literal(lgl->druplig, lglexport(lgl, lit));
		}
		va_end(ap);
		lgldrupligreallydel(lgl);
	}
#else
	(void)lgl;
#endif
}

static void
lgldrupligdelclsaux(LGL* lgl, const int* c)
{
#ifndef NLGLDRUPLIG
	if (lgldruplig(lgl)) {
		const int* p;
		int lit;
		for (p = c; (lit = *p); p++)
			if (lglialiased(lgl, lit))
				return;
		for (p = c; (lit = *p); p++)
			druplig_add_literal(lgl->druplig, lglexport(lgl, lit));
		lgldrupligreallydel(lgl);
		assert(p - c > 1);
	}
#else
	(void)lgl, (void)c;
#endif
}

static void
lgldrupligunit(LGL* lgl, int lit)
{
#ifndef NLGLDRUPLIG
	if (!lgldruplig(lgl))
		return;
	if (lit == lgl->drupligunit)
		return;
	druplig_add_literal(lgl->druplig, lglexport(lgl, lit));
	druplig_check_and_add_redundant_clause(lgl->druplig);
	lgl->drupligunit = lit;
#else
	(void)lgl, (void)lit;
#endif
}

/*------------------------------------------------------------------------*/

#ifndef NDEBUG
static int
lglrgforcing(LGL* lgl, int red, int lidx)
{
	int lit, val, level, truelevel, unit;
	const int *p, *c;
	int *rsn, r0, r1;
	assert(!red || red == REDCS);
	c = lglidx2lits(lgl, red, lidx);
	truelevel = INT_MAX;
	unit = 0;
	for (p = c; (lit = *p); p++) {
		assert(abs(lit) < NOTALIT);
		val = lglval(lgl, lit);
		if (!val)
			return 0;
		level = lglevel(lgl, lit);
		if (val > 0) {
			if (unit)
				return 0;
			truelevel = level;
			unit = lit;
		} else if (level > truelevel)
			return 0;
	}
	if (!unit)
		return 0;
	rsn = lglrsn(lgl, unit);
	r0 = rsn[0];
	if ((r0 & MASKCS) != LRGCS)
		return 0;
	if ((r0 & REDCS) != red)
		return 0;
	r1 = rsn[1];
	if (r1 != lidx)
		return 0;
	return 1;
}
#endif

static void
lglassign(LGL* lgl, int lit, int r0, int r1)
{
	int *p, other, other2, *c, lidx, found;
	int idx, phase, glue, tag, dom, red, irr;
	AVar* av = lglavar(lgl, lit);
	TD* td;
	LOGREASON(2, lit, r0, r1, "assign %d through", lit);
	av->trail = lglcntstk(&lgl->trail);
	if (av->trail >= lgl->szdrail) {
		int newszdrail = lgl->szdrail ? 2 * lgl->szdrail : 1;
		RSZ(lgl->drail, lgl->szdrail, newszdrail);
		lgl->szdrail = newszdrail;
	}
	td = lgltd(lgl, lit);
	tag = r0 & MASKCS;
	dom = (tag == BINCS) ? lglgetdom(lgl, -(r0 >> RMSHFT)) : lit;
	lglsetdom(lgl, lit, dom);
#ifndef NDEBUG
	{
		if (tag == BINCS || tag == TRNCS) {
			other = r0 >> RMSHFT;
			assert(lglval(lgl, other) < 0);
			if (tag == TRNCS) {
				other2 = r1;
				assert(lglval(lgl, other2) < 0);
			}
		} else if (tag == LRGCS) {
			red = r0 & REDCS;
			lidx = r1;
			c = lglidx2lits(lgl, red, lidx);
			found = 0;
			for (p = c; (other = *p); p++)
				if (other == lit)
					found++;
				else
					assert(lglval(lgl, other) < 0);
			assert(found == 1);
		} else
			assert(tag == DECISION || tag == UNITCS);
	}
	assert(!lglval(lgl, lit));
	assert(lgl->unassigned > 0);
	assert(!lgliselim(lgl, lit));
#endif
	idx = abs(lit);
	phase = lglsgn(lit);
	lgl->vals[idx] = phase;
	if (!lgl->simp) {
		lgl->stats->agility -= lgl->stats->agility >> 13;
		if (av->phase != phase) {
			av->phase = phase;
			lgl->stats->agility += ((int64_t)1) << 19;
			lgl->stats->stability.changed++;
		}
	}
#ifndef NDEBUG
	if (phase < 0)
		av->wasfalse = 1;
	else
		av->wasfalse = 0;
#endif
	td->level = lgl->level;
	if (!lgl->level) {
		lgldrupligunit(lgl, lit);
		td->irr = 1;
		if (av->type == EQUIVAR) {
			assert(lgl->stats->equiv.current > 0);
			lgl->stats->equiv.current--;
			assert(lgl->stats->equiv.sum > 0);
			lgl->stats->equiv.sum--;
		} else {
			assert(av->type == FREEVAR);
			av->type = FIXEDVAR;
		}
		lgl->stats->fixed.sum++;
		lgl->stats->fixed.current++;
		lgl->stats->prgss++;
		lgl->stats->irrprgss++;
		td->rsn[0] = UNITCS | RMSHFTLIT(lit);
		td->rsn[1] = 0;
		if (lgl->cbs && lgl->cbs->units.produce.fun) {
			LOG(2, "trying to export internal unit %d external %d\n", lgl->tid, lit, lglexport(lgl, lit));
			lgl->stats->sync.units.produced++;
			lgl->cbs->units.produce.fun(lgl->cbs->units.produce.state, lglexport(lgl, lit));
			LOG(2, "exporting internal unit %d external %d\n", lgl->tid, lit, lglexport(lgl, lit));
		}
	} else {
		td->rsn[0] = r0;
		td->rsn[1] = r1;
		if (lgl->level == 1) {
			assert(tag != UNITCS);
			if (tag == DECISION)
				irr = 1;
			else if ((irr = !(red = (r0 & REDCS)))) {
				if (tag == BINCS) {
					other = r0 >> RMSHFT;
					irr = lgltd(lgl, other)->irr;
				} else if (tag == TRNCS) {
					other = r0 >> RMSHFT;
					if ((irr = lgltd(lgl, other)->irr)) {
						other2 = r1;
						irr = lgltd(lgl, other2)->irr;
					}
				} else {
					assert(tag == LRGCS);
					lidx = r1;
					c = lglidx2lits(lgl, red, lidx);
					found = 0;
					for (p = c; irr && (other = *p); p++)
						if (other == lit)
							found++;
						else
							irr = lgltd(lgl, other)->irr;
					assert(!irr || found == 1);
				}
			}
		} else
			irr = 0;
		td->irr = irr;
	}

	lglpushstk(lgl, &lgl->trail, lit);
	if (!lgl->simp && !lgl->failed && (av->assumed & (1u << (lit > 0)))) {
		LOG(2, "failed assumption %d", -lit);
		lgl->failed = -lit;
	}
	lgl->unassigned--;
	td->lrglue = 0;
	if ((r0 & REDCS) && (r0 & MASKCS) == LRGCS) {
		glue = r1 & GLUEMASK;
		lgl->stats->lir[glue].forcing++;
		assert(lgl->stats->lir[glue].forcing > 0);
		if (lgl->level && 0 < glue && glue < MAXGLUE) {
			lgl->lrgluereasons++;
			assert(lgl->lrgluereasons > 0);
			td->lrglue = 1;
		}
#ifndef NDEBUG
		if (glue == MAXGLUE)
			assert((r1 >> GLUESHFT) + 4 < (int)lglcntstk(&lgl->red[MAXGLUE]));
#endif
	}
#ifndef NDEBUG
	if (tag == LRGCS) {
		lidx = r1;
		red = r0 & REDCS;
		if (lgl->level)
			assert(lglrgforcing(lgl, red, lidx));
		else
			assert(((*lglrsn(lgl, lit)) & MASKCS) == UNITCS);
	}
#endif
}

static void
lglf2rce(LGL* lgl, int lit, int other, int red)
{
	assert(lglval(lgl, other) < 0);
	assert(!red || red == REDCS);
	assert(!lgliselim(lgl, other));
	lglassign(lgl, lit, (RMSHFTLIT(other) | BINCS | red), 0);
}

static void
lglf3rce(LGL* lgl, int lit, int other, int other2, int red)
{
	assert(lglval(lgl, other) < 0);
	assert(lglval(lgl, other2) < 0);
	assert(!lgliselim(lgl, other));
	assert(!lgliselim(lgl, other2));
	assert(!red || red == REDCS);
	lglassign(lgl, lit, (RMSHFTLIT(other) | TRNCS | red), other2);
}

static void
lglflrce(LGL* lgl, int lit, int red, int lidx)
{
#ifndef NDEBUG
	int *p = lglidx2lits(lgl, red, lidx), other;
	while ((other = *p++)) {
		assert(!lgliselim(lgl, other));
		if (other != lit)
			assert(lglval(lgl, other) < 0);
	}
	assert(red == 0 || red == REDCS);
#endif
	lglassign(lgl, lit, red | LRGCS, lidx);
}

static int
lgldscheduled(LGL* lgl, int lit)
{
	QVar* qv = lglqvar(lgl, lit);
	if (qv->enqueued)
		return 1;
	return qv->pos >= 0;
}

static void
lglunassign(LGL* lgl, int lit)
{
	int idx = abs(lit), r0, r1, tag, lidx, glue;
	QVar* qv;
	TD* td;
	LOG(2, "unassign %d", lit);
	assert(lglval(lgl, lit) > 0);
	assert(lgl->vals[idx] == lglsgn(lit));
	lgl->vals[idx] = 0;
	lgl->unassigned++;
	assert(lgl->unassigned > 0);
	qv = lglqvar(lgl, idx);
	if (qv->enqueued)
		assert(qv->pos >= 0);
	else if (qv->pos < 0)
		lgldsched(lgl, idx);
	td = lgltd(lgl, idx);
	r0 = td->rsn[0];
	if (!(r0 & REDCS))
		return;
	tag = r0 & MASKCS;
	if (tag != LRGCS)
		return;
	r1 = td->rsn[1];
	glue = r1 & GLUEMASK;
	if (td->lrglue) {
		assert(lgl->lrgluereasons > 0);
		lgl->lrgluereasons--;
	}
	if (glue < MAXGLUE)
		return;
	lidx = r1 >> GLUESHFT;
	LOG(2, "eagerly deleting maximum glue clause at %d", lidx);
	lglrststk(&lgl->red[glue], lidx);
}

static Val
lglifixed(LGL* lgl, int lit)
{
	int res;
	if (!(res = lglval(lgl, lit)))
		return 0;
	if (lglevel(lgl, lit) > 0)
		return 0;
	return res;
}

static void
lglbacktrack(LGL* lgl, int level)
{
	int lit;
	assert(level >= 0);
	assert(lgl->level > level);
	if (lgl->stats->stability.level > 0) {
		LOG(2, "cancelling restart stability computation");
		lgl->stats->stability.level = 0;
	}
	LOG(2, "backtracking to level %d", level);
	assert(level <= lgl->level);
	assert(abs(lgl->failed) != 1 || lgl->failed == -1);
	if (lgl->failed && lgl->failed != -1 && lglevel(lgl, lgl->failed) > level) {
		LOG(2, "resetting failed assumption %d", lgl->failed);
		lgl->failed = 0;
	}
	while (!lglmtstk(&lgl->trail)) {
		lit = lgltopstk(&lgl->trail);
		assert(abs(lit) > 1);
		if (lglevel(lgl, lit) <= level)
			break;
		lglunassign(lgl, lit);
		lgl->trail.top--;
	}
	if (!level) {
		assert(!lgl->lrgluereasons);
		while (!lglmtstk(&lgl->red[MAXGLUE])) {
			int tmp = lglpopstk(&lgl->red[MAXGLUE]);
			assert(tmp >= NOTALIT);
			(void)tmp;
		}
	}
	if (lgl->alevel > level) {
		LOG(2, "resetting assumption decision level to %d from %d", level, lgl->alevel);
		lgl->alevel = level;
		if (lgl->assumed) {
			LOG(2, "resetting assumption queue level to 0 from %d", lgl->assumed);
			lgl->assumed = 0;
		}
	}
	lgl->level = level;
	lglrstcontrol(lgl, level + 1);
	assert(lglcntctk(&lgl->control) == lgl->level + 1);
	lgl->conf.lit = 0;
	lgl->conf.rsn[0] = lgl->conf.rsn[1] = 0;
	lgl->next2 = lgl->next = lglcntstk(&lgl->trail);
#ifndef NDEBUG
	if (lgl->prevglue == MAXGLUE) {
		lglclnstk(&lgl->prevclause);
		lgl->prevglue = -1;
	}
#endif
	LOG(2, "backtracked ");
}

static int
lglmarked(LGL* lgl, int lit)
{
	int res = lglavar(lgl, lit)->mark;
	if (lit < 0)
		res = -res;
	return res;
}

/*------------------------------------------------------------------------*/

static void
lglunit(LGL* lgl, int lit)
{
	ASSERT(!lgl->level);
	LOG(1, "unit %d", lit);
	lglassign(lgl, lit, RMSHFTLIT(lit) | UNITCS, 0);
}

static void
lglmark(LGL* lgl, int lit)
{
	lglavar(lgl, lit)->mark = lglsgn(lit);
}

static void
lglmarkunmarked(LGL* lgl, int lit)
{
	AVar* av = lglavar(lgl, lit);
	assert(!av->mark);
	av->mark = lglsgn(lit);
}

static void
lglunmark(LGL* lgl, int lit)
{
	lglavar(lgl, lit)->mark = 0;
}

static void
lglchksimpcls(LGL* lgl)
{
#ifndef NDEBUG
	int *p, tmp, lit;
	AVar* av;
	for (p = lgl->clause.start; (lit = *p); p++) {
		tmp = lglifixed(lgl, lit);
		assert(!tmp);
		av = lglavar(lgl, lit);
		assert(!av->simp);
		av->simp = 1;
	}
	while (p > lgl->clause.start)
		lglavar(lgl, *--p)->simp = 0;
#endif
	(void)lgl;
}

static int
lglcval(LGL* lgl, int litorval)
{
	assert(litorval);
	if (litorval == 1 || litorval == -1)
		return litorval;
	return lglval(lgl, litorval);
}

/*------------------------------------------------------------------------*/

static int
lglisimpcls(LGL* lgl)
{
	int *p, *q = lgl->clause.start, lit, satisfied, val, mark;
#ifndef NLGLOG
	int simplified;
#endif

	satisfied = 0;

#ifndef NDEBUG
	for (p = q; (lit = *p); p++)
		assert(!lglmarked(lgl, lit));
#endif

	for (p = q; (lit = *p); p++) {
		if (satisfied)
			continue;
		val = lglval(lgl, lit);
		if (val > 0) {
			LOG(4, "literal %d satisfies clauses", lit);
			satisfied = 1;
			continue;
		}
		if (val < 0) {
			LOG(4, "removing false literal %d", lit);
			continue;
		}
		mark = lglmarked(lgl, lit);
		if (mark > 0) {
			LOG(4, "removing duplicated literal %d", lit);
			continue;
		}
		if (mark < 0) {
			LOG(4, "literals %d and %d occur both", -lit, lit);
			satisfied = 1;
			continue;
		}
		*q++ = lit;
		lglmark(lgl, lit);
	}

	*q = 0;
	lgl->clause.top = q + 1;
#ifndef NLGLOG
	simplified = (lgl->clause.top != q + 1);
#endif

	if (satisfied)
		LOG(2, "simplified clause is trivial");
	else
		LOGCLS(2, lgl->clause.start, "%s simplified clause", simplified ? "changed" : "unchanged");

	while (q > lgl->clause.start)
		lglunmark(lgl, *--q);

	return satisfied;
}

static int
lglesimpcls(LGL* lgl)
{
	int *p, *q = lgl->clause.start, lit, satisfied, val, mark;
#if !defined(NLGLOG) || (!defined(NLGLDRUPLIG) && !defined(NDEBUG))
	int simplified;
#endif

	satisfied = 0;

#ifndef NDEBUG
	for (p = q; (lit = *p); p++)
		assert(abs(lit) == 1 || !lglmarked(lgl, lit));
#endif

	for (p = q; !satisfied && (lit = *p); p++) {
		val = lglcval(lgl, lit);
		if (val > 0) {
			LOG(4, "literal %d satisfies clauses", lit);
			satisfied = 1;
			continue;
		}
		if (val < 0) {
			LOG(4, "removing false literal %d", lit);
			continue;
		}
		mark = lglmarked(lgl, lit);
		if (mark > 0) {
			LOG(4, "removing duplicated literal %d", lit);
			continue;
		}
		if (mark < 0) {
			LOG(4, "literals %d and %d occur both", -lit, lit);
			satisfied = 1;
			continue;
		}
		*q++ = lit;
		lglmark(lgl, lit);
	}

	*q = 0;
	lgl->clause.top = q + 1;
#if !defined(NLGLOG) || (!defined(NLGLDRUPLIG) && !defined(NDEBUG))
	simplified = (lgl->clause.top != q + 1);
#endif

	if (satisfied)
		LOG(2, "simplified clause is trivial");
	else
		LOGCLS(2, lgl->clause.start, "%s simplified clause", simplified ? "changed" : "unchanged");

	while (q > lgl->clause.start)
		lglunmark(lgl, *--q);

#ifndef NLGLDRUPLIG
	if (lgldruplig(lgl)) {
		int different = (lglcntstk(&lgl->eclause) != lglcntstk(&lgl->clause));
		assert(!simplified || different);
		if (!satisfied && different)
			lgldrupligaddcls(lgl, REDCS);
		if (satisfied || different) {
			const int* p;
			int elit;
			for (p = lgl->eclause.start; (elit = *p); p++)
				druplig_add_literal(lgl->druplig, elit);
			lgldrupligreallydel(lgl);
		}
	}
#endif

	return satisfied;
}

static void
lglorderclsaux(LGL* lgl, int* start)
{
	int *p, max = 0, level, lit, val;
	for (p = start; (lit = *p); p++) {
		if ((val = lglval(lgl, lit)) < 0)
			level = lglevel(lgl, lit);
		else if (val > 0)
			level = INT_MAX - 1;
		else
			assert(!val), level = INT_MAX;
		if (level <= max)
			continue;
		max = level;
		*p = start[0];
		start[0] = lit;
	}
}

static void
lglordercls(LGL* lgl)
{
	assert(lglcntstk(&lgl->clause) > 2);
	lglorderclsaux(lgl, lgl->clause.start);
	LOG(3, "head literal %d", lgl->clause.start[0]);
	lglorderclsaux(lgl, lgl->clause.start + 1);
	LOG(3, "tail literal %d", lgl->clause.start[1]);
	LOGCLS(3, lgl->clause.start, "ordered clause");
}
/*------------------------------------------------------------------------*/

static void
lglfreewch(LGL* lgl, unsigned oldoffset, unsigned oldhcount)
{
	unsigned ldoldhcount = lglceild(oldhcount);
	lgl->wchs->stk.start[oldoffset] = (int)lgl->wchs->start[ldoldhcount];
	assert(oldoffset);
	lgl->wchs->start[ldoldhcount] = oldoffset;
	lgl->wchs->free++;
	assert(lgl->wchs->free > 0);
	LOG(5, "saving watch stack at %u of size %u on free list %u", oldoffset, oldhcount, ldoldhcount);
}

static void
lglshrinkhts(LGL* lgl, HTS* hts, int newcount)
{
	unsigned i, oldcount = hts->count;
	int* p;
	assert((unsigned)newcount <= oldcount);
	if ((unsigned)newcount == oldcount)
		return;
	p = lglhts2wchs(lgl, hts);
	for (i = newcount; i < oldcount; i++)
		p[i] = 0;
	hts->count = newcount;
	if (newcount)
		return;
	lglfreewch(lgl, hts->offset, oldcount);
	hts->offset = 0;
}

static ptrdiff_t
lglenlwchs(LGL* lgl, HTS* hts)
{
	unsigned oldhcount, ldoldhcount, ldnewhcount, newhcount;
	unsigned long oldwcount, newwcount, oldwsize, newwsize;
	int *oldwstart, *newwstart, *start;
	unsigned oldoffset, newoffset, i, j;
	ptrdiff_t res;

	res = 0;

	oldhcount = hts->count;
	oldoffset = hts->offset;
	newhcount = 1, ldnewhcount = 0;
	ldoldhcount = lglfloorld(oldhcount);
	assert(ldoldhcount == (unsigned)-1 || ldoldhcount < 32);
	ldnewhcount = ldoldhcount + 1;
	if (ldnewhcount > 31)
		lgldie(lgl, "watcher stack overflow");
	newhcount = (1u << ldnewhcount);

	assert(newhcount > oldhcount);

	LOG(5, "increasing watch stack at %u from %d to %u", oldoffset, oldhcount, newhcount);

	assert(!oldoffset == !oldhcount);

	lgl->stats->enlwchs++;

	newoffset = lgl->wchs->start[ldnewhcount];
	start = lgl->wchs->stk.start;
	if (newoffset != UINT_MAX) {
		lgl->wchs->start[ldnewhcount] = start[newoffset];
		start[newoffset] = 0;
		assert(lgl->wchs->free > 0);
		lgl->wchs->free--;
		LOG(5, "reusing free watch stack at %u of size %u", newoffset, (1u << ldnewhcount));
	} else {
		assert(lgl->wchs->stk.start[hts->offset]);
		assert(lgl->wchs->stk.top[-1] == (int)UINT_MAX);

		oldwcount = lglcntstk(&lgl->wchs->stk);
		newwcount = oldwcount + newhcount;
		if (newwcount > UINT_MAX)
			lgldie(lgl, "watcher stack memory arena overflow");
		oldwsize = lglszstk(&lgl->wchs->stk);
		newwsize = oldwsize;

		assert(lgl->wchs->stk.top == lgl->wchs->stk.start + oldwcount);
		assert(oldwcount > 0);

		while (newwsize < newwcount)
			newwsize *= 2;
		if (newwsize > UINT_MAX) {
			assert(newwcount <= UINT_MAX);
			newwsize = UINT_MAX;
		}
		if (newwsize > oldwsize) {
			newwstart = oldwstart = lgl->wchs->stk.start;
			RSZ(newwstart, oldwsize, newwsize);
			LOG(3, "resized global watcher stack from %lu to %lu", oldwsize, newwsize);
			res = newwstart - oldwstart;
			if (res) {
				LOG(3, "moved global watcher stack by %ld", (long)res);
				start = lgl->wchs->stk.start = newwstart;
			}
			lgl->wchs->stk.end = start + newwsize;
		}
		lgl->wchs->stk.top = start + newwcount;
		lgl->wchs->stk.top[-1] = (int)UINT_MAX;
		newoffset = oldwcount - 1;
		LOG(5, "new watch stack of size %d at end of global watcher stack at %u", newhcount, newoffset);
	}
	assert(start == lgl->wchs->stk.start);
	assert(start[0]);
	j = newoffset;
	for (i = oldoffset; i < oldoffset + oldhcount; i++) {
		start[j++] = start[i];
		start[i] = 0;
	}
	while (j < newoffset + newhcount)
		start[j++] = 0;
	assert(start + j <= lgl->wchs->stk.top);
	hts->offset = newoffset;
	if (oldhcount > 0)
		lglfreewch(lgl, oldoffset, oldhcount);
	return res;
}

static ptrdiff_t
lglpushwch(LGL* lgl, HTS* hts, int wch)
{
	ptrdiff_t res = 0;
	int* wchs = lglhts2wchs(lgl, hts);
	assert(sizeof(res) == sizeof(void*));
	if (wchs[hts->count]) {
		res = lglenlwchs(lgl, hts);
		wchs = lglhts2wchs(lgl, hts);
	}
	assert(!wchs[hts->count]);
	assert(wch != (int)UINT_MAX);
	wchs[hts->count++] = wch;
	lgl->stats->pshwchs++;
	assert(lgl->stats->pshwchs > 0);
	return res;
}

static ptrdiff_t
lglwchbin(LGL* lgl, int lit, int other, int red)
{
	HTS* hts = lglhts(lgl, lit);
	int cs = (RMSHFTLIT(other) | BINCS | red);
	ptrdiff_t res;
	assert(red == 0 || red == REDCS);
	res = lglpushwch(lgl, hts, cs);
	LOG(3, "new %s binary watch %d blit %d", lglred2str(red), lit, other);
	return res;
}

static ptrdiff_t
lglwchtrn(LGL* lgl, int a, int b, int c, int red)
{
	HTS* hts = lglhts(lgl, a);
	int cs = (RMSHFTLIT(b) | TRNCS | red);
	ptrdiff_t res;
	assert(red == 0 || red == REDCS);
	res = lglpushwch(lgl, hts, cs);
	res += lglpushwch(lgl, hts, c);
	LOG(3, "new %s ternary watch %d blits %d %d", lglred2str(red), a, b, c);
	return res;
}

static ptrdiff_t
lglwchlrg(LGL* lgl, int lit, int other, int red, int lidx)
{
	HTS* hts = lglhts(lgl, lit);
	int blit = (RMSHFTLIT(other) | LRGCS | red);
	ptrdiff_t res = 0;
	assert(red == 0 || red == REDCS);
	res += lglpushwch(lgl, hts, blit);
	res += lglpushwch(lgl, hts, lidx);
#ifndef NLGLOG
	{
		int* p = lglidx2lits(lgl, red, lidx);
		if (red)
			LOG(3,
				"watching %d with blit %d in red[%d][%d] %d %d %d %d%s",
				lit,
				other,
				(lidx & GLUEMASK),
				(lidx >> GLUESHFT),
				p[0],
				p[1],
				p[2],
				p[3],
				(p[4] ? " ..." : ""));
		else
			LOG(3,
				"watching %d with blit %d in irr[%d] %d %d %d %d%s",
				lit,
				other,
				lidx,
				p[0],
				p[1],
				p[2],
				p[3],
				(p[4] ? " ..." : ""));
	}
#endif
	assert(sizeof(res) == sizeof(void*));
	return res;
}

/*------------------------------------------------------------------------*/

static EVar*
lglevar(LGL* lgl, int lit)
{
	int idx = abs(lit);
	assert(1 <= idx && idx < lgl->nvars);
	return lgl->evars + idx;
}

static int*
lglepos(LGL* lgl, int lit)
{
	EVar* ev;
	int* res;
	ev = lglevar(lgl, lit);
	res = &ev->pos;
	return res;
}

static int
lglecmp(LGL* lgl, int l, int k)
{
	return lglevar(lgl, k)->score - lglevar(lgl, l)->score;
}

#ifndef NDEBUG
static void
lglchkesched(LGL* lgl)
{
	int *p, parent, child, ppos, cpos, size, i, tmp;
	Stk* s = &lgl->esched;
	size = lglcntstk(s);
	p = s->start;
	for (ppos = 0; ppos < size; ppos++) {
		parent = p[ppos];
		tmp = *lglepos(lgl, parent);
		assert(ppos == tmp);
		for (i = 0; i <= 1; i++) {
			cpos = 2 * ppos + 1 + i;
			if (cpos >= size)
				continue;
			child = p[cpos];
			tmp = *lglepos(lgl, child);
			assert(cpos == tmp);
			assert(lglecmp(lgl, parent, child) >= 0);
		}
	}
}
#endif

static void
lgleup(LGL* lgl, int lit)
{
	int child = lit, parent, cpos, ppos, *p, *cposptr, *pposptr;
	Stk* s = &lgl->esched;
	p = s->start;
	cposptr = lglepos(lgl, child);
	cpos = *cposptr;
	assert(cpos >= 0);
	while (cpos > 0) {
		ppos = (cpos - 1) / 2;
		parent = p[ppos];
		if (lglecmp(lgl, parent, lit) >= 0)
			break;
		pposptr = lglepos(lgl, parent);
		assert(*pposptr == ppos);
		p[cpos] = parent;
		*pposptr = cpos;
		LOGESCHED(5, parent, "down from %d", ppos);
		cpos = ppos;
	}
	if (*cposptr == cpos)
		return;
#ifndef NLGLOG
	ppos = *cposptr;
#endif
	*cposptr = cpos;
	p[cpos] = lit;
	LOGESCHED(5, lit, "up from %d", ppos);
#ifndef NDEBUG
	if (lgl->opts->check.val >= 2)
		lglchkesched(lgl);
#endif
}

static void
lgledown(LGL* lgl, int lit)
{
	int parent = lit, child, right, ppos, cpos;
	int *p, *pposptr, *cposptr, size;
	Stk* s = &lgl->esched;
	size = lglcntstk(s);
	p = s->start;
	pposptr = lglepos(lgl, parent);
	ppos = *pposptr;
	assert(0 <= ppos);
	for (;;) {
		cpos = 2 * ppos + 1;
		if (cpos >= size)
			break;
		child = p[cpos];
		if (cpos + 1 < size) {
			right = p[cpos + 1];
			if (lglecmp(lgl, child, right) < 0)
				cpos++, child = right;
		}
		if (lglecmp(lgl, child, lit) <= 0)
			break;
		cposptr = lglepos(lgl, child);
		assert(*cposptr == cpos);
		p[ppos] = child;
		*cposptr = ppos;
		LOGESCHED(5, child, "up from %d", cpos);
		ppos = cpos;
	}
	if (*pposptr == ppos)
		return;
#ifndef NLGLOG
	cpos = *pposptr;
#endif
	*pposptr = ppos;
	p[ppos] = lit;
	LOGESCHED(5, lit, "down from %d", cpos);
#ifndef NDEBUG
	if (lgl->opts->check.val >= 2)
		lglchkesched(lgl);
#endif
}

static int
lglifrozen(LGL* lgl, int ilit)
{
	int elit = lglexport(lgl, ilit);
	Ext* ext = lglelit2ext(lgl, elit);
	return ext->frozen || ext->tmpfrozen;
}

static void
lglesched(LGL* lgl, int lit)
{
	AVar* av;
	int* p;
	Stk* s;
	if (lgl->cceing)
		return;
	if (lglifrozen(lgl, lit))
		return;
	if (!lglisfree(lgl, lit))
		return;
	if (lgl->donotsched) {
		av = lglavar(lgl, lit);
		if (lgl->eliminating && av->donotelm)
			return;
		if (lgl->blocking && av->donotblk)
			return;
	}
	p = lglepos(lgl, lit);
	s = &lgl->esched;
	if (*p >= 0)
		return;
	*p = lglcntstk(s);
	lglpushstk(lgl, s, lit);
	lgleup(lgl, lit);
	lgledown(lgl, lit);
	LOGESCHED(4, lit, "pushed");
}

/*------------------------------------------------------------------------*/

static unsigned
lglgcd(unsigned a, unsigned b)
{
	unsigned tmp;
	assert(a), assert(b);
	if (a < b)
		SWAP(unsigned, a, b);
	while (b)
		tmp = b, b = a % b, a = tmp;
	return a;
}

static int
lglrandidxtrav(LGL* lgl, int (*fun)(LGL*, int idx))
{
	int idx, delta, mod, prev, first, res;
	first = mod = lglmax(lgl->nvars, 2);
	idx = lglrand(lgl) % mod;
	delta = lglrand(lgl) % mod;
	if (!delta)
		delta++;
	while (lglgcd(delta, mod) > 1)
		if (++delta == mod)
			delta = 1;
	res = 1;
	LOG(2, "random index traversal start %d delta %d mod %d", idx, delta, mod);
	while (res) {
		if (idx >= 2 && !fun(lgl, idx))
			res = 0;
		else {
			prev = idx;
			idx += delta;
			if (idx >= mod)
				idx -= mod;
			if (idx == first)
				break;
			if (first == mod)
				first = prev;
		}
	}
	return res;
}

static int
lglrem(LGL* lgl)
{
	int res = lgl->nvars;
	if (!res)
		return 0;
	assert(res >= 2);
	res -= lgl->stats->fixed.current + 2;
	assert(res >= 0);
	return res;
}

static double
lglpcnt(double n, double d)
{
	if (d <= 0 || !n)
		return 0.0;
	return 100.0 * n / d;
}

static int
lglecalc(LGL* lgl, EVar* ev)
{
	int oldscore = ev->score, o0 = ev->occ[0], o1 = ev->occ[1], newscore;
	if (!o0 || !o1)
		newscore = 0;
	else
		newscore = o0 + o1;
	ev->score = newscore;
	(void)lgl;
	return newscore - oldscore;
}

static int
lglocc(LGL* lgl, int lit)
{
	if (!lgl->occs)
		return lglhts(lgl, lit)->count;
	return lglevar(lgl, lit)->occ[lit < 0];
}

static void
lglflushtouched(LGL* lgl)
{
	int *q, idx, mt = 0, *pos = lgl->elm->touched.pos, newpos = 0;
	Stk* touched = &lgl->elm->touched.stk;
	const int* p;
	for (p = q = touched->start; p < touched->top; p++) {
		if ((idx = *p)) {
			assert(1 < idx), assert(idx < lgl->elm->touched.nvars);
			pos[idx] = newpos++;
			*q++ = idx;
		} else
			mt++;
	}
	LOG(2, "flushed %d empty entries on touched stack kept %d", mt, newpos);
	assert(lgl->elm->touched.mt == mt);
	lgl->elm->touched.mt = 0;
	touched->top = q;
}

static void
lgltouch(LGL* lgl, int lit)
{
	int idx = abs(lit), *posptr, oldpos, newpos;
	assert(lgl->elm);
	posptr = lgl->elm->touched.pos + idx;
	newpos = lglcntstk(&lgl->elm->touched.stk);
	oldpos = *posptr;
	assert(oldpos < 0 || lglpeek(&lgl->elm->touched.stk, oldpos) == idx);
	LOG(4, "touching %d (moved to %d from %d)", idx, newpos, oldpos);
	lglpushstk(lgl, &lgl->elm->touched.stk, idx);
	*posptr = newpos;
	if (oldpos >= 0) {
		lglpoke(&lgl->elm->touched.stk, oldpos, 0);
		lgl->elm->touched.mt++;
		if (lgl->elm->touched.mt > lgl->elm->touched.nvars)
			lglflushtouched(lgl);
	}
}

static int
lglpoptouched(LGL* lgl)
{
#if !defined(NDEBUG) || !defined(NLGLOG)
	int pos;
#endif
	int res, *posptr;
	assert(lgl->elm);
	for (;;) {
		if (lglmtstk(&lgl->elm->touched.stk))
			return 0;
		if ((res = lglpopstk(&lgl->elm->touched.stk)))
			break;
		assert(lgl->elm->touched.mt > 0);
		lgl->elm->touched.mt--;
	}
#if !defined(NDEBUG) || !defined(NLGLOG)
	pos = lglcntstk(&lgl->elm->touched.stk);
#endif
	assert(1 < res), assert(res < lgl->elm->touched.nvars);
	posptr = lgl->elm->touched.pos + res;
	assert(*posptr >= 0), assert(*posptr == pos);
	*posptr = -1;
	LOG(4, "popped touched %d at position %d", res, pos);
	return res;
}

static int
lgltouched(LGL* lgl, int lit)
{
	int idx = abs(lit);
	assert(1 < idx), assert(idx < lgl->elm->touched.nvars);
	return lgl->elm->touched.pos[idx] >= 0;
}

static void
lglincocc(LGL* lgl, int lit)
{
	int idx, sign, change;
	EVar* ev;
	if (!lgl->occs)
		return;
	idx = abs(lit), sign = (lit < 0);
	ev = lglevar(lgl, lit);
	assert(lglisfree(lgl, lit));
	ev->occ[sign] += 1;
	assert(ev->occ[sign] > 0);
	change = lglecalc(lgl, ev);
	LOG(3, "inc occ of %d now occs[%d] = %d %d", lit, idx, ev->occ[0], ev->occ[1]);
	if (ev->pos < 0)
		lglesched(lgl, idx);
	else if (change > 0)
		lgledown(lgl, idx);
	else if (change < 0)
		lgleup(lgl, idx);
	if (lgl->touching)
		lgltouch(lgl, lit);
}

static int
lglisact(int act)
{
	return NOTALIT <= act && act < REMOVED - 1;
}

static int
lglrescoreglue(LGL* lgl, int glue)
{
	int *c, *p, oldact, newact, count = 0;
	const int retireint = lgl->opts->retireint.val;
	Stk* lir = lgl->red + glue;
	for (c = lir->start; c < lir->top; c = p + 1) {
		oldact = *c;
		if (oldact == REMOVED) {
			for (p = c + 1; p < lir->top && *p == REMOVED; p++)
				;
			assert(p >= lir->top || *p < NOTALIT || lglisact(*p));
			p--;
		} else if (NOTALIT + retireint < oldact) {
			assert(NOTALIT + retireint <= oldact && oldact <= REMOVED - 1);
			newact = (oldact - NOTALIT - retireint + 1) / 2;
			newact += NOTALIT + retireint;
			assert(newact < REMOVED);
			*c++ = newact;
			LOGCLS(5, c, "rescoring activity from %d to %d of clause", oldact - NOTALIT, newact - NOTALIT);
			for (p = c; *p; p++)
				;
			count++;
		} else {
			for (p = c + 1; *p; p++)
				;
		}
	}
	return count;
}

static void
lglrescoreclauses(LGL* lgl)
{
	int glue, count = 0;
	lgl->stats->rescored.clauses++;
	for (glue = 0; glue < MAXGLUE; glue++)
		count += lglrescoreglue(lgl, glue);
	lglprt(lgl, 3, "[rescored-clauses-%d] rescored activity of %d clauses", lgl->stats->rescored.clauses, count);
}

static void
lglchkirrstats(LGL* lgl)
{
#if 0
  int idx, sign, lit, blit, tag, red, other, other2, clauses, lits;
  const int * p, * w, * eow, * c, * top;
  HTS * hts;
  clauses = lits = 0;
  for (idx = 2; idx < lgl->nvars; idx++)
    for (sign = -1; sign <= 1; sign += 2) {
      lit = sign * idx;
      hts = lglhts (lgl, lit);
      w = lglhts2wchs (lgl, hts);
      eow = w + hts->count;
      for (p = w; p < eow; p++) {
	blit = *p;
	tag = blit & MASKCS;
	if (tag == TRNCS || tag == LRGCS) p++;
	red = blit & REDCS;
	if (red) continue;
	if (tag == BINCS) {
	  other = blit >> RMSHFT;
	  if (abs (other) < idx) continue;
	  lits += 2;
	} else if (tag == TRNCS) {
	  other = blit >> RMSHFT;
	  if (abs (other) < idx) continue;
	  other2 = *p;
	  if (abs (other2) < idx) continue;
	  lits += 3;
	} else continue;
	clauses++;
      }
    }
  top = lgl->irr.top;
  for (c = lgl->irr.start; c < top; c = p + 1) {
    if (*(p = c) >= NOTALIT) continue;
    while (*++p)
      ;
    lits += p - c;
    clauses++;
  }
  assert (clauses == lgl->stats->irr.clauses.cur);
  assert (lits == lgl->stats->irr.lits.cur);
#else
	(void)lgl;
#endif
}

static void
lglincirr(LGL* lgl, int size)
{
	if (size < 2)
		return;
	lgl->stats->irr.clauses.cur++;
	assert(lgl->stats->irr.clauses.cur > 0);
	if (lgl->stats->irr.clauses.cur > lgl->stats->irr.clauses.max)
		lgl->stats->irr.clauses.max = lgl->stats->irr.clauses.cur;
	lgl->stats->irr.lits.cur += size;
	assert(lgl->stats->irr.lits.cur >= size);
	if (lgl->stats->irr.lits.cur > lgl->stats->irr.lits.max)
		lgl->stats->irr.lits.max = lgl->stats->irr.lits.cur;
	lgl->stats->irrprgss++;
}

static void
lgldecirr(LGL* lgl, int size)
{
	assert(size >= 2);
	assert(lgl->stats->irr.clauses.cur > 0);
	lgl->stats->irr.clauses.cur--;
	assert(lgl->stats->irr.lits.cur >= size);
	lgl->stats->irr.lits.cur -= size;
	assert(!lgl->stats->irr.clauses.cur == !lgl->stats->irr.lits.cur);
	lgl->stats->irrprgss++;
}

static void
lglincglag(LGL* lgl)
{
	if (lgl->glag == MAXGLAG) {
		int idx;
		for (idx = 2; idx < lgl->nvars; idx++)
			lglqvar(lgl, idx)->glag = -1;
		lgl->glag = 0;
	} else
		lgl->glag++;
	LOG(4, "new glue flag %d", lgl->glag);
}

static int
lglaged(LGL* lgl, int lit)
{
	int level = lglevel(lgl, lit);
	QVar* qv;
	if (!level++)
		return 1;
	assert(2 <= level), assert(level < lgl->nvars);
	qv = lglqvar(lgl, level);
	assert(qv->glag <= lgl->glag);
	if (qv->glag >= lgl->glag)
		return 1;
	assert(0 <= lgl->glag), assert(lgl->glag <= MAXGLAG);
	qv->glag = lgl->glag;
	assert(0 <= qv->glag);
	return 0;
}

static const int lglar1scalegluetab[POW2GLUE] = {
	// 0  1  2  3  4  5  6  7  8  9  10  11  12  13  14  15
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
};

static const int lglar2scalegluetab[POW2GLUE] = {
	// 0  1  2  3   4   5   6   7   8   9  10  11  12  13   14   15
	0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105, 120
};

static const int lglsqrtscalegluetab[POW2GLUE] = {
	// 0  1  2  3   4   5   6   7   8   9  10  11    12   13   14   15
	0, 1, 2, 5, 10, 17, 26, 37, 50, 65, 82, 101, 122, 145, 170, 197
};

static const int lglsqrtldscalegluetab[POW2GLUE] = {
	// 0  1  2  3   4   5   6   7   8   9  10   11   12   13    14    15
	0, 1, 2, 5, 10, 17, 26, 37, 50, 65, 82, 122, 226, 530, 1522, 5042
};

static const int lglldscalegluetab[POW2GLUE] = {
	// 0  1  2  3   4   5   6    7    8    9    10    11    12    13     14     15
	0, 1, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768
};

static int
lglsearchscaleglue(int unscaled, const int* tab)
{
	int res;
	for (res = 0; res < POW2GLUE && tab[res + 1] <= unscaled; res++)
		;
	return res;
}

#define LGLSEARCHSCALEGLUE(NAME, GLUE) lglsearchscaleglue(GLUE, lgl##NAME##scalegluetab)

static int
lglunboundedscaleglue(LGL* lgl, int origlue)
{
	int res, red;
	assert(0 <= origlue);
	if (origlue <= lgl->opts->gluekeep.val)
		res = 0;
	else {
		red = origlue - lgl->opts->gluekeep.val;
		assert(red >= 1);
		switch (lgl->opts->gluescale.val) {
			default:
			case 1:
				res = LGLSEARCHSCALEGLUE(ar1, red);
				break;
			case 2:
				res = LGLSEARCHSCALEGLUE(ar2, red);
				break;
			case 3:
				res = LGLSEARCHSCALEGLUE(sqrt, red);
				break;
			case 4:
				res = LGLSEARCHSCALEGLUE(sqrtld, red);
				break;
			case 5:
				res = LGLSEARCHSCALEGLUE(ld, red);
				break;
		}
		assert(res > 0);
		if (res > MAXGLUE)
			res = MAXGLUE;
	}
	assert(0 <= res), assert(res <= MAXGLUE);
	return res;
}

static int
lglscaleglue(LGL* lgl, int origlue)
{
	int res = lglunboundedscaleglue(lgl, origlue);
	if (lgl->opts->maxscaledglue.val <= res)
		res = MAXGLUE;
	assert(0 <= res), assert(res <= MAXGLUE);
	return res;
}

static int
lglunscaleglue(LGL* lgl, int scaledglue)
{
	int res = 0;
	assert(0 <= scaledglue), assert(scaledglue <= MAXGLUE);
	if (!scaledglue)
		res = 0;
	else {
		switch (lgl->opts->gluescale.val) {
			default:
			case 1:
				res = lglar1scalegluetab[scaledglue];
				break;
			case 2:
				res = lglar2scalegluetab[scaledglue];
				break;
			case 3:
				res = lglsqrtldscalegluetab[scaledglue];
				break;
			case 4:
				res = lglsqrtldscalegluetab[scaledglue];
				break;
			case 5:
				res = lglldscalegluetab[scaledglue];
				break;
		}
		res += lgl->opts->gluekeep.val;
	}
	assert(lglunboundedscaleglue(lgl, res) == scaledglue);
	assert(!res || lglunboundedscaleglue(lgl, res - 1) == scaledglue - 1);
	return res;
}

static int
lglcheckgluereduced(LGL* lgl, int lidx, int gluelim, int sizelim, int* newunscaledglueptr)
{
	int oldscaledglue, newunscaledglue, newscaledglue, lit, pos, size;
	const int* c;
	Stk* lir;
	oldscaledglue = (lidx & GLUEMASK);
	if (!oldscaledglue)
		return 0;
	if (oldscaledglue >= MAXGLUE)
		return 0;
	lglincglag(lgl);
	lir = lgl->red + oldscaledglue;
	pos = lidx >> GLUESHFT;
	assert(pos > 0);
	c = lir->start + pos;
	if (c > lir->top)
		return 0;
	if (c[0] >= REMOVED)
		return 0;
	assert(lir->start < c && c < lir->end);
	size = newunscaledglue = 0;
	while ((lit = c[size])) {
		if (++size > sizelim)
			return 0;
		if (lglaged(lgl, lit))
			continue;
		if (++newunscaledglue > gluelim)
			return 0;
	}
	newscaledglue = lglscaleglue(lgl, newunscaledglue);
	if (oldscaledglue <= newscaledglue)
		return 0;
	LOG(4,
		"old scaled glue %d size %d clause at %d has new scaled glue %d"
		" (unscaled %d)",
		oldscaledglue,
		size,
		pos,
		newscaledglue,
		newunscaledglue);
	if (newunscaledglueptr)
		*newunscaledglueptr = newunscaledglue;
	return 1;
}

static void
lglcheckpromotion(LGL* lgl, int lidx)
{
	const int gluelim = lgl->opts->promotegluelim.val;
	int glue;
	if (!lgl->opts->promote.val)
		return;
	glue = lidx & GLUEMASK;
	if (!glue)
		return;
	if (glue >= MAXGLUE)
		return;
	if (!lglcheckgluereduced(lgl, lidx, gluelim, INT_MAX, 0))
		return;
	LOG(4, "saving clause idx red[%d][%d] on promotion stack", glue, (lidx >> GLUESHFT));
	lglpushstk(lgl, &lgl->promote, lidx);
	lgl->stats->promote.tried++;
}

static void
lglbumplidx(LGL* lgl, int lidx)
{
	int glue = (lidx & GLUEMASK), *c, *ap, act, pos;
	const int retirenb = lgl->opts->retirenb.val;
	const int retireint = lgl->opts->retireint.val;
	Stk* lir = lgl->red + glue;
	if (lgl->simp && !lgl->opts->bumpsimp.val)
		return;
	if (glue >= MAXGLUE)
		return;
	pos = lidx >> GLUESHFT;
	c = lir->start + pos;
	assert(lir->start < c && c < lir->end);
	ap = c - 1;
	act = *ap;
	if (act < REMOVED - 1) {
		LGLCHKACT(act);
		act += 1;
		if (retirenb && act < NOTALIT + retireint)
			act = NOTALIT + retireint;
		LGLCHKACT(act);
		*ap = act;
	}
	LOGCLS(4, c, "bumped activity to %d of glue %d clause", act - NOTALIT, glue);
	lgl->stats->lir[glue].resolved++;
	assert(lgl->stats->lir[glue].resolved > 0);
	if (act >= REMOVED - 1)
		lglrescoreclauses(lgl);
}

static void
lglbumpnupdatelidx(LGL* lgl, int lidx)
{
	lglbumplidx(lgl, lidx);
	lglcheckpromotion(lgl, lidx);
}

static void
lglincjwh(LGL* lgl, int lit, Flt inc)
{
	int ulit = lglulit(lit);
	Flt old = lgl->jwh[ulit];
	Flt new = lgladdflt(old, inc);
	lgl->jwh[ulit] = new;
}

static void
lglmtaux(LGL* lgl, int red)
{
	assert(!red || red == REDCS);
	if (lgl->mt)
		return;
	LOG(1, "adding empty clause");
	lgl->mt = 1;
	lgldrupligaddclsarg(lgl, red, 0);
}

static void
lglmt(LGL* lgl)
{
	lglmtaux(lgl, REDCS);
}

static int
lgladdcls(LGL* lgl, int red, int origlue, int force)
{
	int size, lit, other, other2, *p, lidx, unit, blit;
	int64_t *maxbytesptr, bytes;
	int scaledglue, prevglue;
	Flt inc;
	Val val;
	Stk* w;
	lgl->stats->prgss++;
	if (lgl->eliminating) {
		size = lglcntstk(&lgl->clause);
		ADDSTEPS(elm.steps, size);
	}
	if (!red)
		lgl->stats->irrprgss++;
	assert(!lglmtstk(&lgl->clause));
	assert(!lgl->clause.top[-1]);
	if (force)
		lglchksimpcls(lgl);
	size = lglcntstk(&lgl->clause) - 1;
	if (!red)
		lglincirr(lgl, size);
	else if (size == 2)
		lgl->stats->red.bin++, assert(lgl->stats->red.bin > 0);
	else if (size == 3)
		lgl->stats->red.trn++, assert(lgl->stats->red.trn > 0);
	assert(size >= 0);
	if (!size) {
		lglmtaux(lgl, red);
		return 0;
	}
	lit = lgl->clause.start[0];
	if (size == 1) {
		assert(lglval(lgl, lit) >= 0);
		if (!lglval(lgl, lit))
			lglunit(lgl, lit);
		return 0;
	}
	inc = lglflt(-size, 1);
	other = lgl->clause.start[1];
	if (size == 2) {
		lglwchbin(lgl, lit, other, red);
		lglwchbin(lgl, other, lit, red);
		// if (red)
		{
			if (force && lglval(lgl, lit) < 0)
				lglf2rce(lgl, other, lit, red);
			if (force && lglval(lgl, other) < 0)
				lglf2rce(lgl, lit, other, red);
		}
		if (!red && lgl->dense) {
			assert(!red);
			lglincocc(lgl, lit);
			lglincocc(lgl, other);
		}
		if (!red)
			lglincjwh(lgl, lit, inc), lglincjwh(lgl, other, inc);
		return 0;
	}
	lglordercls(lgl);
	lit = lgl->clause.start[0];
	other = lgl->clause.start[1];
	if (size == 3) {
		other2 = lgl->clause.start[2];
		lglwchtrn(lgl, lit, other, other2, red);
		lglwchtrn(lgl, other, lit, other2, red);
		lglwchtrn(lgl, other2, lit, other, red);
		// if (red)
		{
			if (force && lglval(lgl, lit) < 0 && lglval(lgl, other) < 0)
				lglf3rce(lgl, other2, lit, other, red);
			if (force && lglval(lgl, lit) < 0 && lglval(lgl, other2) < 0)
				lglf3rce(lgl, other, lit, other2, red);
			if (force && lglval(lgl, other) < 0 && lglval(lgl, other2) < 0)
				lglf3rce(lgl, lit, other, other2, red);
		}
		if (!red && lgl->dense) {
			assert(!red);
			lglincocc(lgl, lit);
			lglincocc(lgl, other);
			lglincocc(lgl, other2);
		}
		if (!red)
			lglincjwh(lgl, lit, inc), lglincjwh(lgl, other, inc), lglincjwh(lgl, other2, inc);
		return 0;
	}
	assert(size > 3);
	if (red) {
		scaledglue = lglscaleglue(lgl, origlue);
		if (scaledglue == MAXGLUE && lgl->opts->keepmaxglue.val &&
			!(lgl->stats->clauses.maxglue.count % lgl->opts->keepmaxglueint.val)) {
			lgl->stats->clauses.maxglue.count++;
			lgl->stats->clauses.maxglue.kept++;
			scaledglue = MAXGLUE - 1;
			LOG(2, "reducing maximum glue %d to %d to keep clause", MAXGLUE, scaledglue);
		}
		lgl->stats->clauses.scglue += scaledglue;
		if (scaledglue == MAXGLUE) {
			lgl->stats->clauses.maxglue.count++;
#ifndef NDEBUG
			lglclnstk(&lgl->prevclause);
			lgl->prevglue = -1;
#endif
		} else
			lgl->stats->clauses.nonmaxglue++;
		w = lgl->red + scaledglue;
		lidx = lglcntstk(w) + 1;
		if (lidx > MAXREDLIDX) {
			prevglue = scaledglue;
			if (lidx > MAXREDLIDX) {
				scaledglue = prevglue;
				while (scaledglue + 1 < MAXGLUE && lidx > MAXREDLIDX) {
					w = lgl->red + ++scaledglue;
					lidx = lglcntstk(w) + 1;
				}
			}
			if (lidx > MAXREDLIDX) {
				scaledglue = prevglue;
				while (scaledglue > 0 && lidx > MAXREDLIDX) {
					w = lgl->red + --scaledglue;
					lidx = lglcntstk(w) + 1;
				}
			}
			if (lidx > MAXREDLIDX && scaledglue < MAXGLUE) {
				w = lgl->red + (scaledglue = MAXGLUE);
				lidx = lglcntstk(w) + 1;
			}
			if (lidx > MAXREDLIDX && scaledglue == MAXGLUE) {
				lglbacktrack(lgl, 0);
				lidx = lglcntstk(w);
				assert(!lidx);
			}
			if (lidx > MAXREDLIDX)
				lgldie(lgl, "number of redundant large clause literals exhausted");
		}
		assert(w == lgl->red + scaledglue);
		maxbytesptr = &lgl->stats->lir[scaledglue].maxbytes;
		lglpushstk(lgl, w, NOTALIT);
		assert(lidx == (int)lglcntstk(w));
		lidx <<= GLUESHFT;
		assert(0 <= lidx);
		lidx |= scaledglue;
		if (scaledglue < MAXGLUE) {
			lgl->stats->lir[scaledglue].clauses++;
			assert(lgl->stats->lir[scaledglue].clauses > 0);
		}
		lgl->stats->lir[scaledglue].added++;
		assert(lgl->stats->lir[scaledglue].added > 0);
	} else {
		w = &lgl->irr;
		maxbytesptr = &lgl->stats->irr.maxbytes;
		lidx = lglcntstk(w);
		scaledglue = 0;
		if (lidx <= 0 && !lglmtstk(w))
			lgldie(lgl, "number of irredundant large clause literals exhausted");
	}
	for (p = lgl->clause.start; (other2 = *p); p++)
		lglpushstk(lgl, w, other2), lglincjwh(lgl, other2, inc);
	lglpushstk(lgl, w, 0);
	bytes = sizeof(int) * (int64_t)lglcntstk(w);
	if (bytes > *maxbytesptr)
		*maxbytesptr = bytes;
	// if (red)
	{
		unit = 0;
		for (p = lgl->clause.start; (other2 = *p); p++) {
			val = lglval(lgl, other2);
			assert(!force || val <= 0);
			if (val < 0)
				continue;
			if (unit)
				unit = INT_MAX;
			else
				unit = other2;
		}
		if (force && unit && unit != INT_MAX)
			lglflrce(lgl, unit, red, lidx);
	}
	assert(red == 0 || red == REDCS);
	if (!red || (red && scaledglue < MAXGLUE)) {
		(void)lglwchlrg(lgl, lit, other, red, lidx);
		(void)lglwchlrg(lgl, other, lit, red, lidx);
	}
	if (red && scaledglue != MAXGLUE) {
		// lglbumplidx (lgl, lidx); 		// TODO why was this here?
		lgl->stats->red.lrg++;
	}
	if (!red && lgl->dense) {
		if (lidx > MAXIRRLIDX)
			lgldie(lgl, "number of irredundant large clause literals exhausted");
		blit = (lidx << RMSHFT) | OCCS;
		for (p = lgl->clause.start; (other2 = *p); p++) {
			lglincocc(lgl, other2);
			(void)lglpushwch(lgl, lglhts(lgl, other2), blit);
		}
	}
	lglchkirrstats(lgl);
	return lidx;
}

static void
lgliadd(LGL* lgl, int ilit)
{
	int size;
#ifndef NLGLOG
	if (lglmtstk(&lgl->clause))
		LOG(4, "opening irredundant clause");
#endif
	assert(abs(ilit) < 2 || abs(ilit) < lgl->nvars);
	lglpushstk(lgl, &lgl->clause, ilit);
	if (ilit) {
		LOG(4, "added literal %d", ilit);
	} else {
		LOG(4, "closing irredundant clause");
		LOGCLS(3, lgl->eclause.start, "external irredundant clause");
		LOGCLS(3, lgl->clause.start, "internal unsimplified irredundant clause");
#ifndef NLGLDRUPLIG
		if (lgldruplig(lgl)) {
			const int* p;
			int other;
			for (p = lgl->eclause.start; (other = *p); p++)
				druplig_add_literal(lgl->druplig, other);
			lgldrupligreallyadd(lgl, 0);
		}
#endif
		if (!lglesimpcls(lgl)) {
			lgladdcls(lgl, 0, 0, 1);
			lgl->stats->irr.clauses.add++;
			size = lglcntstk(&lgl->clause) - 1;
			assert(size >= 0);
			lgl->stats->irr.lits.add += size;
		}
		lglclnstk(&lgl->clause);
		lglclnstk(&lgl->eclause);
	}
}

static void
lgleunassignall(LGL* lgl)
{
	int eidx;
	for (eidx = 1; eidx <= lgl->maxext; eidx++)
		lglelit2ext(lgl, eidx)->val = 0;
}

static void
lglchkeassumeclean(LGL* lgl)
{
	assert(lglmtstk(&lgl->eassume));
#ifndef NDEBUG
	int eidx;
	for (eidx = 1; eidx <= lgl->maxext; eidx++) {
		Ext* ext = lglelit2ext(lgl, eidx);
		assert(!ext->assumed);
		assert(!ext->failed);
	}
#endif
	(void)lgl;
}

static void
lglchkassumeclean(LGL* lgl)
{
	assert(!lgl->failed);
	assert(!lgl->assumed);
	assert(lglmtstk(&lgl->assume));
#ifndef NDEBUG
	if (lgl->opts->check.val >= 1) {
		int idx;
		for (idx = 2; idx < lgl->nvars; idx++) {
			AVar* av = lglavar(lgl, idx);
			assert(!av->assumed);
			assert(!av->failed);
		}
	}
#endif
	(void)lgl;
}

static void
lglreset(LGL* lgl)
{
	int elit, ilit, erepr;
	Ext *ext, *rext;
	unsigned bit;
	AVar* av;
	if (lgl->state == RESET)
		return;
	if (lgl->state <= USED)
		return;
	assert(lgl->state & (UNKNOWN | SATISFIED | EXTENDED | UNSATISFIED | FAILED | LOOKED));
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	if (!lglmtstk(&lgl->eassume)) {
		LOG(2, "resetting %d external assumptions", lglcntstk(&lgl->eassume));
		while (!lglmtstk(&lgl->eassume)) {
			elit = lglpopstk(&lgl->eassume);
			ext = lglelit2ext(lgl, elit);
			ext->assumed = 0;
			if (ext->failed) {
				ext->failed = 0;
				erepr = lglerepr(lgl, elit);
				if (erepr != elit) {
					rext = lglelit2ext(lgl, erepr);
					rext->failed = 0;
				}
			}
		}
	}
	lglchkeassumeclean(lgl);
	if (!lglmtstk(&lgl->assume)) {
		LOG(2, "resetting %d internal assumptions", lglcntstk(&lgl->assume));
		while (!lglmtstk(&lgl->assume)) {
			ilit = lglpopstk(&lgl->assume);
			av = lglavar(lgl, ilit);
			bit = (1u << (ilit < 0));
			assert(av->assumed & bit);
			av->assumed &= ~bit;
			av->failed &= ~bit;
		}
	}
	if (lgl->failed) {
		LOG(2, "resetting internal failed assumption %d", lgl->failed);
		lgl->failed = 0;
	}
	if (lgl->assumed) {
		LOG(2, "resetting assumption queue level to 0 from %d", lgl->assumed);
		lgl->assumed = 0;
	}
	lglchkassumeclean(lgl);
	lgleunassignall(lgl);
	if (lgl->cbs && lgl->cbs->term.done) {
		LOG(2, "resetting forced termination done flag");
		lgl->cbs->term.done = 0;
	}
	TRANS(RESET);
}

static void
lgluse(LGL* lgl)
{
	if (lgl->state >= USED)
		return;
	assert(lgl->state == UNUSED || lgl->state == OPTSET);
	TRANS(USED);
}

static void
lglupdatealiased(LGL* lgl, int elit)
{
	Ext* ext;
	int erepr;
	assert(elit), assert(elit != INT_MIN);
	if (abs(elit) > lgl->maxext)
		return;
	erepr = lglerepr(lgl, elit);
	if (erepr == elit)
		return;
	ext = lglelit2ext(lgl, erepr);
	if (ext->aliased)
		return;
	LOG(4, "external literal %d aliased by external literal %d", erepr, elit);
	ext->aliased = 1;
}

static void
lgleadd(LGL* lgl, int elit)
{
	int ilit;
	lglreset(lgl);
	if (elit) {
		lglupdatealiased(lgl, elit);
		ilit = lglimport(lgl, elit);
		LOG(4, "adding external literal %d as %d", elit, ilit);
	} else {
		ilit = 0;
		LOG(4, "closing external clause");
	}
	lglpushstk(lgl, &lgl->eclause, elit);
	lgliadd(lgl, ilit);
#ifndef NCHKSOL
	lglpushstk(lgl, &lgl->orig, elit);
#endif
}

void
lgladd(LGL* lgl, int elit)
{
	int eidx = abs(elit);
	Ext* ext;
	REQINITNOTFORKED();
	TRAPI("add %d", elit);
	if (0 < eidx && eidx <= lgl->maxext) {
		ext = lglelit2ext(lgl, elit);
		ABORTIF(ext->melted, "adding melted literal %d", elit);
	}
	lgl->stats->calls.add++;
	lgleadd(lgl, elit);
	lgluse(lgl);
	if (lgl->clone)
		lgladd(lgl->clone, elit);
}

static void
lglisetphase(LGL* lgl, int lit, int phase)
{
	AVar* av;
	if (lit < 0)
		lit = -lit, phase = -phase;
	av = lglavar(lgl, lit);
	av->fase = phase;
	LOG(2, "setting phase of internal literal %d to %d", lit, phase);
}

static void
lglesetphase(LGL* lgl, int elit, int phase)
{
	int ilit = lglimport(lgl, elit);
	if (abs(ilit) >= 2) {
		LOG(2, "setting phase of external literal %d to %d", elit, phase);
		lglisetphase(lgl, ilit, phase);
	} else
		LOG(2, "setting phase of external literal %d skipped", elit);
}

void
lglsetphase(LGL* lgl, int elit)
{
	REQINITNOTFORKED();
	TRAPI("setphase %d", elit);
	ABORTIF(!elit, "invalid literal argument");
	if (elit < 0)
		lglesetphase(lgl, -elit, -1);
	else
		lglesetphase(lgl, elit, 1);
	if (lgl->clone)
		lglsetphase(lgl->clone, elit);
}

void
lglresetphase(LGL* lgl, int elit)
{
	REQINITNOTFORKED();
	TRAPI("resetphase %d", elit);
	ABORTIF(!elit, "invalid literal argument");
	lglesetphase(lgl, elit, 0);
	if (lgl->clone)
		lglresetphase(lgl->clone, elit);
}

static void
lglisetimportant(LGL* lgl, int lit)
{
	QVar* qv;
	qv = lglqvar(lgl, lit);
	qv->important = 1;
	LOG(2, "setting variable %d to be important", abs(lit));
}

static void
lglesetimportant(LGL* lgl, int elit)
{
	int ilit = lglimport(lgl, elit);
	if (abs(ilit) >= 2) {
		LOG(2, "setting external literal %d to be important", elit);
		lglisetimportant(lgl, ilit);
	} else
		LOG(2, "setting external literal %d to be important skipped", elit);
}

void
lglsetimportant(LGL* lgl, int elit)
{
	REQINITNOTFORKED();
	TRAPI("setimportant %d", elit);
	ABORTIF(!elit, "invalid literal argument");
	if (elit < 0)
		lglesetphase(lgl, -elit, -1);
	else
		lglesetimportant(lgl, elit);
	if (lgl->clone)
		lglsetimportant(lgl->clone, elit);
}

static void
lgleassume(LGL* lgl, int elit)
{
	int ilit, val;
	unsigned bit;
	AVar* av;
	Ext* ext;
	lglreset(lgl);
	ilit = lglimport(lgl, elit);
	LOG(2, "assuming external literal %d", elit);
	bit = 1u << (elit < 0);
	ext = lglelit2ext(lgl, elit);
	if (!(ext->assumed & bit)) {
		ext->assumed |= bit;
		lglpushstk(lgl, &lgl->eassume, elit);
	}
	assert(!lgl->level);
	if (!(val = lglcval(lgl, ilit))) {
		av = lglavar(lgl, ilit);
		bit = (1u << (ilit < 0));
		if (av->assumed & bit) {
			LOG(2, "internal literal %d already assumed", ilit);
		} else {
			av->assumed |= bit;
			if (av->assumed & (bit ^ 3))
				LOG(2, "negation %d was also already assumed", -ilit);
			lglpushstk(lgl, &lgl->assume, ilit);
		}
	} else if (val > 0) {
		LOG(2, "externally assumed literal %d already fixed to true", elit);
	} else {
		assert(val < 0);
		LOG(2, "externally assumed literal %d already fixed to false", elit);
		if (ilit != -1) {
			av = lglavar(lgl, ilit);
			bit = (1u << (ilit < 0));
			if (!(av->assumed & bit)) {
				av->assumed |= bit;
				lglpushstk(lgl, &lgl->assume, ilit);
			}
		}
		if (!lgl->failed)
			lgl->failed = ilit;
	}
}

static void
lglecassume(LGL* lgl, int elit)
{
	LOG(2, "adding external literal %d to assumed clause", elit);
	(void)lgl;
	(void)elit;
}

void
lglassume(LGL* lgl, int elit)
{
	int eidx = abs(elit);
	Ext* ext;
	REQINITNOTFORKED();
	TRAPI("assume %d", elit);
	lgl->stats->calls.assume++;
	ABORTIF(!elit, "can not assume invalid literal 0");
	if (0 < eidx && eidx <= lgl->maxext) {
		ext = lglelit2ext(lgl, elit);
		ABORTIF(ext->melted, "assuming melted literal %d", elit);
	}
	lgleassume(lgl, elit);
	lgluse(lgl);
	lglmelter(lgl);
	if (lgl->clone)
		lglassume(lgl->clone, elit);
}

void
lglcassume(LGL* lgl, int elit)
{
	int eidx = abs(elit);
	Ext* ext;
	REQINITNOTFORKED();
	TRAPI("cassume %d", elit);
	lgl->stats->calls.cassume++;
	if (0 < eidx && eidx <= lgl->maxext) {
		ext = lglelit2ext(lgl, elit);
		ABORTIF(ext->melted, "assuming melted literal %d", elit);
	}
	lglecassume(lgl, elit);
	lgluse(lgl);
	if (lgl->clone)
		lglcassume(lgl->clone, elit);
}

void
lglfixate(LGL* lgl)
{
	const int* p;
	Stk eassume;
	REQINITNOTFORKED();
	TRAPI("fixate");
	if (lgl->mt)
		return;
	CLR(eassume);
	for (p = lgl->eassume.start; p < lgl->eassume.top; p++)
		lglpushstk(lgl, &eassume, *p);
	for (p = eassume.start; p < eassume.top; p++)
		lgleadd(lgl, *p), lgleadd(lgl, 0);
	lglrelstk(lgl, &eassume);
	lgluse(lgl);
	if (lgl->clone)
		lglfixate(lgl->clone);
}

static void
lglbonflict(LGL* lgl, int lit, int blit)
{
	assert(lglevel(lgl, lit) >= lglevel(lgl, blit >> RMSHFT));
	assert(!lgliselim(lgl, blit >> RMSHFT));
	assert(!lgliselim(lgl, lit));
	lgl->conf.lit = lit;
	lgl->conf.rsn[0] = blit;
	LOG(2, "inconsistent %s binary clause %d %d", lglred2str(blit & REDCS), lit, (blit >> RMSHFT));
}

static void
lgltonflict(LGL* lgl, int lit, int blit, int other2)
{
	assert((blit & MASKCS) == TRNCS);
	assert(lglevel(lgl, lit) >= lglevel(lgl, blit >> RMSHFT));
	assert(lglevel(lgl, lit) >= lglevel(lgl, other2));
	assert(!lgliselim(lgl, blit >> RMSHFT));
	assert(!lgliselim(lgl, other2));
	assert(!lgliselim(lgl, lit));
	lgl->conf.lit = lit;
	lgl->conf.rsn[0] = blit;
	lgl->conf.rsn[1] = other2;
	LOG(2, "inconsistent %s ternary clause %d %d %d", lglred2str(blit & REDCS), lit, (blit >> RMSHFT), other2);
}

static void
lglonflict(LGL* lgl, int check, int lit, int red, int lidx)
{
	int glue;
#if !defined(NLGLOG) || !defined(NDEBUG)
	int *p, *c = lglidx2lits(lgl, red, lidx);
#endif
	assert(red == REDCS || !red);
#ifndef NDEBUG
	{
		int found = 0;
		for (p = c; *p; p++) {
			if (*p == lit)
				found++;
			assert(lglval(lgl, *p) <= -check);
			assert(lglevel(lgl, lit) >= lglevel(lgl, *p));
			assert(!lgliselim(lgl, lit));
		}
		assert(found == 1);
	}
#endif
	lgl->conf.lit = lit;
	lgl->conf.rsn[0] = red | LRGCS;
	lgl->conf.rsn[1] = lidx;
#ifndef NLGLOG
	if (lgl->opts->log.val >= 2) {
		lglogstart(lgl, 2, "inconsistent %s large clause", lglred2str(red));
		for (p = c; *p; p++)
			fprintf(lgl->out, " %d", *p);
		lglogend(lgl);
	}
#endif
	if (red) {
		glue = lidx & GLUEMASK;
		lgl->stats->lir[glue].conflicts++;
		assert(lgl->stats->lir[glue].conflicts > 0);
	}
	(void)lgl;
	(void)check;
}

static void
lgldeclscnt(LGL* lgl, int size, int red, int glue)
{
	assert(!red || red == REDCS);
	if (!red)
		lgldecirr(lgl, size);
	else if (size == 2)
		assert(lgl->stats->red.bin), lgl->stats->red.bin--;
	else if (size == 3)
		assert(lgl->stats->red.trn), lgl->stats->red.trn--;
	else {
		assert(lgl->stats->red.lrg > 0);
		lgl->stats->red.lrg--;
		assert(lgl->stats->lir[glue].clauses > 0);
		lgl->stats->lir[glue].clauses--;
	}
}

static void
lglrminc(LGL* lgl, const int* w, const int* eow)
{
	int inc;
	assert(w <= eow);
	assert(sizeof w < 8 || eow <= w + INT_MAX);
	inc = eow - w;
	assert(inc >= 0);
	inc >>= lgl->opts->rmincpen.val;
	inc++;
	assert(lgl->blocking + lgl->eliminating + lgl->simpleprobing <= 1);
	if (lgl->blocking)
		ADDSTEPS(blk.steps, inc);
	else if (lgl->eliminating)
		ADDSTEPS(elm.steps, inc);
	else if (lgl->simpleprobing)
		ADDSTEPS(prb.simple.steps, inc);
}

static void
lglrmtwch(LGL* lgl, int lit, int other1, int other2, int red)
{
	int *p, blit, other, blit1, blit2, *w, *eow, tag;
	HTS* hts;
	assert(!red || red == REDCS);
	LOG(3, "removing %s ternary watch %d blits %d %d", lglred2str(red), lit, other1, other2);
	hts = lglhts(lgl, lit);
	assert(hts->count >= 2);
	p = w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	lglrminc(lgl, w, eow);
	blit1 = RMSHFTLIT(other1) | red | TRNCS;
	blit2 = RMSHFTLIT(other2) | red | TRNCS;
	for (;;) {
		assert(p < eow);
		blit = *p++;
		tag = blit & MASKCS;
		if (tag == BINCS || tag == OCCS)
			continue;
		other = *p++;
		if (tag == LRGCS)
			continue;
		assert(tag == TRNCS);
		if (blit == blit1 && other == other2)
			break;
		if (blit == blit2 && other == other1)
			break;
	}
	while (p < eow)
		p[-2] = p[0], p++;
	lglshrinkhts(lgl, hts, p - w - 2);
}

static void
lglpopnunmarkstk(LGL* lgl, Stk* stk)
{
	while (!lglmtstk(stk)) {
		int lit = lglpopstk(stk);
		if (!lit)
			continue;
		lglavar(lgl, lit)->mark = 0;
	}
}

static void
lglpopnunlcamarkstk(LGL* lgl, Stk* stk)
{
	while (!lglmtstk(stk))
		lglavar(lgl, lglpopstk(stk))->lcamark = 0;
}

static int
lglcamarked(LGL* lgl, int lit)
{
	switch (lglavar(lgl, lit)->lcamark) {
		case 1:
			return (lit < 0) ? -1 : 1;
		case 2:
			return (lit < 0) ? -2 : 2;
		case 4:
			return (lit < 0) ? 1 : -1;
		case 8:
			return (lit < 0) ? 2 : -2;
		default:
			assert(!lglavar(lgl, lit)->lcamark);
			return 0;
	}
}

static void
lglcamark(LGL* lgl, int lit, int mark)
{
	int newmark;
	AVar* av;
	assert(mark == 1 || mark == 2);
	av = lglavar(lgl, lit);
	assert(!av->lcamark);
	newmark = mark;
	if (lit < 0)
		newmark <<= 2;
	av->lcamark = newmark;
	lglpushstk(lgl, &lgl->lcaseen, lit);
	assert(lglcamarked(lgl, lit) == mark);
	assert(lglcamarked(lgl, -lit) == -mark);
}

static int
lgbiglca(LGL* lgl, int a, int b, int64_t* stepsptr)
{
	int blit, tag, mark, negmark, prevmark, c, res, prev, next;
	const int *p, *w, *eow;
	int64_t steps;
	HTS* hts;
	assert(a), assert(b);
	assert(abs(a) != abs(b));
	assert(lglevel(lgl, a));
	assert(lglevel(lgl, b));
	assert(lglval(lgl, a) > 0);
	assert(lglval(lgl, b) > 0);
	assert(lglgetdom(lgl, a) == lglgetdom(lgl, b));
	assert(lglmtstk(&lgl->lcaseen));
	lglcamark(lgl, a, 1);
	lglcamark(lgl, b, 2);
	res = next = 0;
	steps = 0;
	while (next < (int)lglcntstk(&lgl->lcaseen)) {
		c = lglpeek(&lgl->lcaseen, next++);
		assert(lglval(lgl, c) > 0);
		assert(lglevel(lgl, c) > 0);
		mark = lglcamarked(lgl, c);
		assert(mark == 1 || mark == 2);
		negmark = mark ^ 3;
		hts = lglhts(lgl, c);
		if (!hts->count)
			continue;
		w = lglhts2wchs(lgl, hts);
		eow = w + hts->count;
		steps++;
		for (p = w; p < eow; p++) {
			blit = *p;
			tag = blit & MASKCS;
			if (tag == TRNCS || tag == LRGCS)
				p++;
			if (tag != BINCS)
				continue;
			prev = -(blit >> RMSHFT);
			if (!lglevel(lgl, prev))
				continue;
			if (lglval(lgl, prev) <= 0)
				continue;
			if ((prevmark = lglcamarked(lgl, prev)) < 0)
				continue;
			if (mark == prevmark)
				continue;
			if (prevmark == negmark) {
				res = prev;
				goto DONE;
			}
			lglcamark(lgl, prev, mark);
		}
	}
DONE:
	lglpopnunlcamarkstk(lgl, &lgl->lcaseen);
	LOG(3, "least common ancestor of %d and %d is %d", a, b, res);
	*stepsptr += steps;
	return res;
}

static int
lglimplca(LGL* lgl, int a, int b, int64_t* stepsptr)
{
	int r0, tag, mark, negmark, prevmark, c, res, prev, next;
	int64_t steps;
	assert(a), assert(b);
	assert(abs(a) != abs(b));
	assert(lglevel(lgl, a));
	assert(lglevel(lgl, b));
	assert(lglval(lgl, a) > 0);
	assert(lglval(lgl, b) > 0);
	assert(lglgetdom(lgl, a) == lglgetdom(lgl, b));
	assert(lglmtstk(&lgl->lcaseen));
	lglcamark(lgl, a, 1);
	lglcamark(lgl, b, 2);
	res = next = 0;
	steps = 0;
	while (next < (int)lglcntstk(&lgl->lcaseen)) {
		c = lglpeek(&lgl->lcaseen, next++);
		assert(lglval(lgl, c) > 0);
		assert(lglevel(lgl, c) > 0);
		mark = lglcamarked(lgl, c);
		assert(mark == 1 || mark == 2);
		negmark = mark ^ 3;
		steps++;
		r0 = *lglrsn(lgl, c);
		tag = r0 & MASKCS;
		if (tag != BINCS)
			continue;
		prev = -(r0 >> RMSHFT);
		if (!lglevel(lgl, prev))
			continue;
		if (lglval(lgl, prev) <= 0)
			continue;
		if ((prevmark = lglcamarked(lgl, prev)) < 0)
			continue;
		if (mark == prevmark)
			continue;
		if (prevmark == negmark) {
			res = prev;
			goto DONE;
		}
		lglcamark(lgl, prev, mark);
	}
DONE:
	lglpopnunlcamarkstk(lgl, &lgl->lcaseen);
	LOG(3, "least common ancestor of %d and %d is %d", a, b, res);
	*stepsptr += steps;
	return res;
}

static int
lglhbrdom(LGL* lgl, int a, int b, int64_t* stepsptr)
{
	assert(lglgetdom(lgl, a) == lglgetdom(lgl, b));
	switch (lgl->opts->hbrdom.val) {
		case 2:
			return lgbiglca(lgl, a, b, stepsptr);
		case 1:
			return lglimplca(lgl, a, b, stepsptr);
		default:
			return lglgetdom(lgl, a);
	}
}

static void
lglrmlwch(LGL* lgl, int lit, int red, int lidx)
{
	int blit, tag, *p, *q, *w, *eow, ored, olidx;
	HTS* hts;
#ifndef NLGLOG
	p = lglidx2lits(lgl, red, lidx);
	if (red)
		LOG(3,
			"removing watch %d in red[%d][%d] %d %d %d %d%s",
			lit,
			(lidx & GLUEMASK),
			(lidx >> GLUESHFT),
			p[0],
			p[1],
			p[2],
			p[3],
			(p[4] ? " ..." : ""));
	else
		LOG(3, "removing watch %d in irr[%d] %d %d %d %d%s", lit, lidx, p[0], p[1], p[2], p[3], (p[4] ? " ..." : ""));
#endif
	hts = lglhts(lgl, lit);
	assert(hts->count >= 2);
	p = w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	lglrminc(lgl, w, eow);
	for (;;) {
		assert(p < eow);
		blit = *p++;
		tag = blit & MASKCS;
		if (tag == BINCS)
			continue;
		if (tag == OCCS) {
			assert(lgl->dense);
			continue;
		}
		olidx = *p++;
		if (tag == TRNCS)
			continue;
		assert(tag == LRGCS);
		ored = blit & REDCS;
		if (ored != red)
			continue;
		if (olidx == lidx)
			break;
	}
	assert((p[-2] & REDCS) == red);
	assert(p[-1] == lidx);
	for (q = p; q < eow; q++)
		q[-2] = q[0];
	lglshrinkhts(lgl, hts, q - w - 2);
}

static void
lglpropsearch(LGL* lgl, int lit)
{
	int *q, *eos, blit, other, other2, other3, red, prev;
	int tag, val, val2, lidx, *c, *l;
	const int* p;
	ptrdiff_t delta;
	int visits;
	HTS* hts;

	LOG(3, "propagating %d in search", lit);

	assert(!lgl->simp);
	assert(!lgl->lkhd);
	assert(!lgl->probing);
	assert(!lgl->dense);
	assert(!lgliselim(lgl, lit));
	assert(lglval(lgl, lit) == 1);

	hts = lglhts(lgl, -lit);
	if (!hts->offset)
		return;
	q = lglhts2wchs(lgl, hts);
	eos = q + hts->count;
	visits = 0;
	for (p = q; p < eos; p++) {
		visits++;
		*q++ = blit = *p;
		tag = blit & MASKCS;
		if (tag != BINCS) {
			assert(tag == TRNCS || tag == LRGCS);
			*q++ = *++p;
		}
		other = (blit >> RMSHFT);
		assert(abs(other) != abs(lit));
		val = lglval(lgl, other);
		if (val > 0)
			continue;
		red = blit & REDCS;
		if (tag == BINCS) {
			if (val < 0) {
				lglbonflict(lgl, -lit, blit);
				p++;
				break;
			}
			lglf2rce(lgl, other, -lit, red);
		} else if (tag == TRNCS) {
			other2 = *p;
			val2 = lglval(lgl, other2);
			if (val2 > 0)
				continue;
			if (!val && !val2)
				continue;
			if (val < 0 && val2 < 0) {
				lgltonflict(lgl, -lit, blit, other2);
				p++;
				break;
			}
			if (!val)
				SWAP(int, other, other2);
			else
				assert(val < 0);
			lglf3rce(lgl, other2, -lit, other, red);
		} else {
			assert(tag == LRGCS);
			assert(val <= 0);
			lidx = *p;
			c = lglidx2lits(lgl, red, lidx);
			other2 = c[0];
			if (other2 == -lit)
				other2 = c[0] = c[1], c[1] = -lit;
			else
				assert(c[1] == -lit);
			if (other2 != other) {
				other = other2;
				val = lglval(lgl, other);
				if (val > 0) {
					q[-2] = LRGCS | RMSHFTLIT(other2) | red;
					continue;
				}
			}
			assert(!red || !lgliselim(lgl, other));
			val2 = 0;
			prev = -lit;
			for (l = c + 2; (other2 = *l); l++) {
				*l = prev;
				val2 = lglval(lgl, other2);
				if (val2 >= 0)
					break;
				assert(!red || !lgliselim(lgl, other2));
				prev = other2;
			}
			assert(!other2 || val2 >= 0);
			if (other2) {
				c[1] = other2;
				assert(other == c[0]);
				delta = lglwchlrg(lgl, other2, other, red, lidx);
				if (delta)
					p += delta, q += delta, eos += delta;
				q -= 2;
				continue;
			}
			while (l > c + 2) {
				other3 = *--l;
				*l = prev;
				prev = other3;
			}
			if (val < 0) {
				lglonflict(lgl, 1, -lit, red, lidx);
				p++;
				break;
			}
			assert(!val);
			lglflrce(lgl, other, red, lidx);
		}
	}
	while (p < eos)
		*q++ = *p++;
	lglshrinkhts(lgl, hts, hts->count - (p - q));
	assert(!lgl->simp);

	lgl->stats->visits.search += visits;
}

static int
lglhbred(LGL* lgl, int subsumed, int red)
{
	int res = subsumed ? red : REDCS;
	LOG(3, "hyber binary learned clause becomes %s", lglred2str(res));
	(void)lgl;
	return res;
}

static void
lgldecocc(LGL*, int); // TODO move scheduling ...

static void
lglrmlocc(LGL* lgl, int lit, int red, int lidx)
{
	int search, blit, tag, *p, *q, *w, *eow;
	HTS* hts;
#ifndef NLGLOG
	if (red)
		LOG(3, "removing occurrence %d in red[0][%d]", lit, lidx);
	else
		LOG(3, "removing occurrence %d in irr[%d]", lit, lidx);
#endif
	assert(!red || red == REDCS);
	hts = lglhts(lgl, lit);
	assert(hts->count >= 1);
	assert(lidx <= MAXIRRLIDX);
	search = (lidx << RMSHFT) | OCCS | red;
	assert(search >= 0);
	p = w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	lglrminc(lgl, w, eow);
	do {
		assert(p < eow);
		blit = *p++;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
	} while (blit != search);
	assert(p[-1] == search);
	for (q = p; q < eow; q++)
		q[-1] = q[0];
	lglshrinkhts(lgl, hts, q - w - 1);
}

static void
lglflushremovedoccs(LGL* lgl, int lit)
{
	HTS* hts = lglhts(lgl, lit);
	int* w = lglhts2wchs(lgl, hts);
	int* eow = w + hts->count;
	int blit, tag, red, lidx;
	int *p, *q, *c;
	lglrminc(lgl, w, eow);
	for (p = q = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		red = blit & REDCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == BINCS)
			*q++ = blit;
		else if (tag == TRNCS)
			*q++ = blit, *q++ = *p;
		else {
			assert(tag == LRGCS || tag == OCCS);
			if (!red) {
				lidx = (tag == LRGCS) ? *p : (blit >> RMSHFT);
				c = lglidx2lits(lgl, red, lidx);
				if (c[0] == REMOVED)
					continue;
			}
			*q++ = blit;
			if (tag == LRGCS)
				*q++ = *p;
		}
	}
	lglshrinkhts(lgl, hts, q - w);
}

static void
lglpropupdvisits(LGL* lgl, int visits, int64_t steps)
{
	if (lgl->lkhd)
		lgl->stats->visits.lkhd += visits;
	else if (lgl->simp)
		lgl->stats->visits.simp += visits;
	else
		lgl->stats->visits.search += visits;

	if (lgl->simp) {
		if (lgl->basicprobing)
			ADDSTEPS(prb.basic.steps, visits + steps);
		else if (lgl->simpleprobing)
			ADDSTEPS(prb.simple.steps, visits + steps);
		else if (lgl->treelooking)
			ADDSTEPS(prb.treelook.steps, visits + steps);
		else if (lgl->sweeping)
			ADDSTEPS(sweep.steps, visits + steps);
	} else {
		assert(!lgl->basicprobing);
		assert(!lgl->simpleprobing);
		assert(!lgl->treelooking);
		assert(!lgl->sweeping);
	}
}

static void
lglprop(LGL* lgl, int lit)
{
	int *p, *q, *eos, blit, other, other2, other3, red, prev;
	int tag, val, val2, lidx, *c, *l, dom, hbred, subsumed;
	int glue, flushoccs, visits;
	int64_t steps;
	ptrdiff_t delta;
	HTS* hts;
	LOG(3, "propagating %d over ternary and large clauses", lit);
	assert(!lgliselim(lgl, lit));
	assert(lglval(lgl, lit) == 1);
	hts = lglhts(lgl, -lit);
	if (!hts->offset)
		return;
	flushoccs = 0;
	q = lglhts2wchs(lgl, hts);
	eos = q + hts->count;
	visits = 0;
	steps = 0;
	for (p = q; p < eos; p++) {
		blit = *p;
		tag = blit & MASKCS;
		red = blit & REDCS;
		if (tag == OCCS) {
			assert(lgl->dense);
			assert(!red);
			*q++ = blit;
			continue;
		}
		visits++;
		other = (blit >> RMSHFT);
		val = lglval(lgl, other);
		if (tag == BINCS) {
			*q++ = blit;
			if (val > 0)
				continue;
			if (red && lgliselim(lgl, other))
				continue;
			if (val < 0) {
				lglbonflict(lgl, -lit, blit);
				p++;
				break;
			}
			assert(!val);
			lglf2rce(lgl, other, -lit, red);
		} else if (tag == TRNCS) {
			*q++ = blit;
			other2 = *++p;
			*q++ = other2;
			if (val > 0)
				continue;
			if (red && lgliselim(lgl, other))
				continue;
			val2 = lglval(lgl, other2);
			if (val2 > 0)
				continue;
			if (!val && !val2)
				continue;
			if (red && lgliselim(lgl, other2))
				continue;
			if (val < 0 && val2 < 0) {
				lgltonflict(lgl, -lit, blit, other2);
				p++;
				break;
			}
			if (!val)
				SWAP(int, other, other2);
			else
				assert(val < 0);
			if (lgl->level && lgl->simp && lgl->opts->lhbr.val) {
				assert(lgl->simp);
				dom = lglgetdom(lgl, lit);
				if (lglgetdom(lgl, -other) != dom)
					goto NO_HBR_JUST_F3RCE;
				dom = lglhbrdom(lgl, lit, -other, &steps);
				if (!dom)
					goto NO_HBR_JUST_F3RCE;
				subsumed = (dom == lit || dom == -other);
				hbred = lglhbred(lgl, subsumed, red);
				LOG(2, "hyper binary resolved %s clause %d %d", lglred2str(hbred), -dom, other2);
				lgldrupligaddclsarg(lgl, REDCS, -dom, other2, 0);
				if (subsumed) {
					LOG(2, "subsumes %s ternary clause %d %d %d", lglred2str(red), -lit, other, other2);
					lglrmtwch(lgl, other2, other, -lit, red);
					lglrmtwch(lgl, other, other2, -lit, red);
					lgl->stats->hbr.sub++;
					if (red)
						assert(lgl->stats->red.trn), lgl->stats->red.trn--;
					else {
						lgldecirr(lgl, 3);
						if (lgl->dense) {
							if (-dom == -lit)
								lgldecocc(lgl, other);
							else {
								assert(-dom == other);
								lgldecocc(lgl, -lit);
							}
						}
					}
					lgldrupligdelclsarg(lgl, -lit, other, other2, 0);
				}
				delta = 0;
				if (dom == lit) {
					LOG(3,
						"replacing %s ternary watch %d blits %d %d with binary %d blit %d",
						lglred2str(red),
						-lit,
						other,
						other2,
						-dom,
						other2);
					assert(subsumed);
					blit = RMSHFTLIT(other2) | BINCS | hbred;
					q[-2] = blit;
					q--;
				} else {
					if (dom == -other) {
						LOG(3, "removing %s ternary watch %d blits %d %d", lglred2str(red), -lit, other, other2);
						assert(subsumed);
						q -= 2;
					} else {
						LOG(2,
							"replaces %s ternary clause %d %d %d as reason for %d",
							lglred2str(red),
							-lit,
							other,
							other2,
							other2);
						assert(!subsumed);
						assert(abs(dom) != abs(lit));
						assert(abs(other2) != abs(lit));
					}
					delta += lglwchbin(lgl, -dom, other2, hbred);
				}
				delta += lglwchbin(lgl, other2, -dom, hbred);
				if (delta)
					p += delta, q += delta, eos += delta;
				if (hbred)
					lgl->stats->red.bin++, assert(lgl->stats->red.bin > 0);
				else
					lglincirr(lgl, 2);
				lglf2rce(lgl, other2, -dom, hbred);
				lgl->stats->hbr.trn++;
				lgl->stats->hbr.cnt++;
				lgl->stats->prgss++;
			} else {
			NO_HBR_JUST_F3RCE:
				lglf3rce(lgl, other2, -lit, other, red);
			}
		} else {
			assert(tag == LRGCS);
			if (val > 0)
				goto COPYL;
			lidx = p[1];
			c = lglidx2lits(lgl, red, lidx);
			other2 = c[0];
			if (other2 >= NOTALIT) {
				p++;
				continue;
			}
			if (other2 == -lit)
				other2 = c[0] = c[1], c[1] = -lit;
			if (other2 != other) {
				other = other2;
				val = lglval(lgl, other);
				blit = red;
				blit |= LRGCS;
				blit |= RMSHFTLIT(other2);
				if (val > 0)
					goto COPYL;
			}
			if (red && lgliselim(lgl, other))
				goto COPYL;
			val2 = INT_MAX;
			prev = -lit;
			for (l = c + 2; (other2 = *l); l++) {
				*l = prev;
				val2 = lglval(lgl, other2);
				if (val2 >= 0)
					break;
				if (red && lgliselim(lgl, other2))
					break;
				prev = other2;
			}
			assert(val2 != INT_MAX);
			if (other2 && val2 >= 0) {
				c[1] = other2;
				assert(other == c[0]);
				delta = lglwchlrg(lgl, other2, other, red, lidx);
				if (delta)
					p += delta, q += delta, eos += delta;
				p++;
				continue;
			}

			while (l > c + 2) {
				other3 = *--l;
				*l = prev;
				prev = other3;
			}

			if (other2 && val2 < 0)
				goto COPYL;

			if (val < 0) {
				lglonflict(lgl, 1, -lit, red, lidx);
				break;
			}

			assert(!val);
			if (lgl->level && lgl->simp && lgl->opts->lhbr.val) {
				assert(lgl->simp);
				dom = 0;
				for (l = c; (other2 = *l); l++) {
					if (other2 == other)
						continue;
					if (!lglevel(lgl, other2))
						continue;
					assert(lglval(lgl, other2) < 0);
					if (!dom)
						dom = lglgetdom(lgl, -other);
					if (dom != lglgetdom(lgl, -other2))
						goto NO_HBR_JUST_FLRCE;
				}
				LOGCLS(2, c, "dominator %d for %s clause", dom, lglred2str(red));
				dom = 0;
				for (l = c; (other2 = *l); l++) {
					if (other2 == other)
						continue;
					if (!lglevel(lgl, other2))
						continue;
					dom = lglhbrdom(lgl, dom, -other2, &steps);
				}
				if (!dom)
					goto NO_HBR_JUST_FLRCE;
				LOGCLS(2, c, "closest dominator %d", dom);
				subsumed = 0;
				for (l = c; !subsumed && (other2 = *l); l++)
					subsumed = (dom == -other2);
				assert(lit != dom || subsumed);
				hbred = lglhbred(lgl, subsumed, red);
				LOG(2, "hyper binary resolved %s clause %d %d", lglred2str(hbred), -dom, other);
				lgldrupligaddclsarg(lgl, REDCS, -dom, other, 0);
				if (subsumed) {
					LOGCLS(2, c, "subsumes %s large clause", lglred2str(red));
					lglrmlwch(lgl, other, red, lidx);
					lgl->stats->hbr.sub++;
					if (red) {
						glue = lidx & GLUEMASK;
						if (glue != MAXGLUE) {
							assert(lgl->stats->red.lrg);
							lgl->stats->red.lrg--;
							assert(lgl->stats->lir[glue].clauses > 0);
							lgl->stats->lir[glue].clauses--;
						}
					}
					if (!red && lgl->dense) {
						for (l = c; (other2 = *l); l++) {
							if (other2 != -lit)
								lglrmlocc(lgl, other2, 0, lidx);
							if (other2 == -dom)
								continue;
							if (other2 == other)
								continue;
							lgldecocc(lgl, other2);
						}
						flushoccs++;
					}
					if (red && glue < MAXGLUE) {
						LGLCHKACT(c[-1]);
						c[-1] = REMOVED;
					}
					lgldrupligdelclsaux(lgl, c);
					for (l = c; *l; l++)
						*l = REMOVED;
					if (!red)
						lgldecirr(lgl, l - c);
					*l = REMOVED;
				}
				delta = 0;
				if (dom == lit) {
					assert(subsumed);
					LOG(3,
						"replacing %s large watch %d with binary watch %d blit %d",
						lglred2str(red),
						-lit,
						-lit,
						-dom);
					blit = RMSHFTLIT(other) | BINCS | hbred;
					*q++ = blit, p++;
				} else {
					if (subsumed) {
						LOG(3, "removing %s large watch %d", lglred2str(red), -lit);
						p++;
					} else {
						LOGCLS(2,
							   c,
							   "%s binary clause %d %d becomes reasons "
							   "for %d instead of %s large clause",
							   lglred2str(hbred),
							   -dom,
							   other,
							   other,
							   lglred2str(red));
						assert(hbred == REDCS);
					}
					delta += lglwchbin(lgl, -dom, other, hbred);
				}
				delta += lglwchbin(lgl, other, -dom, hbred);
				if (delta)
					p += delta, q += delta, eos += delta;
				if (hbred)
					lgl->stats->red.bin++, assert(lgl->stats->red.bin > 0);
				else
					lglincirr(lgl, 2);
				lglf2rce(lgl, other, -dom, hbred);
				lgl->stats->hbr.lrg++;
				lgl->stats->hbr.cnt++;
				lgl->stats->prgss++;
				if (subsumed)
					continue;
			} else {
			NO_HBR_JUST_FLRCE:
				lglflrce(lgl, other, red, lidx);
			}
		COPYL:
			*q++ = blit;
			*q++ = *++p;
		}
	}
	while (p < eos)
		*q++ = *p++;
	lglshrinkhts(lgl, hts, hts->count - (p - q));
	if (flushoccs)
		lglflushremovedoccs(lgl, -lit);
	lglpropupdvisits(lgl, visits, steps);
}

static void
lglprop2(LGL* lgl, int lit)
{
	int other, blit, tag, val, red, visits;
	const int *p, *w, *eow;
	int64_t travs;
	HTS* hts;
	LOG(3, "propagating %d over binary clauses", lit);
	assert(!lgliselim(lgl, lit));
	assert(lglval(lgl, lit) == 1);
	visits = 0;
	travs = 0;
	hts = lglhts(lgl, -lit);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag != BINCS)
			continue;
		visits++;
		red = blit & REDCS;
		other = blit >> RMSHFT;
		travs++;
		if (lgliselim(lgl, other)) {
			assert(red);
			continue;
		}
		val = lglval(lgl, other);
		if (val > 0)
			continue;
		if (val < 0) {
			lglbonflict(lgl, -lit, blit);
			break;
		}
		lglf2rce(lgl, other, -lit, red);
	}

	lglpropupdvisits(lgl, visits, travs);
}

static int
lglhasconflict(LGL* lgl)
{
	return lgl->conf.lit != 0;
}

static int
lglbcpcomplete(LGL* lgl)
{
	assert(lgl->next2 <= lgl->next);
	assert(lgl->next <= (int)lglcntstk(&lgl->trail));
	if (lgl->next2 < lgl->next)
		return 0;
	if (lgl->next < (int)lglcntstk(&lgl->trail))
		return 0;
	return 1;
}

static void
lglchkbcpclean(LGL* lgl, const char* where)
{
	ASSERT(lglbcpcomplete(lgl));
	ASSERT(!lgl->conf.lit);
	ASSERT(!lgl->mt);
	(void)where;
	(void)lgl;
}

static int
lglbcp(LGL* lgl)
{
	int lit, trail, count;
	assert(!lgl->mt);
	assert(!lgl->conf.lit);
	assert(!lgl->notfullyconnected);
	count = 0;
	while (!lgl->conf.lit) {
		trail = lglcntstk(&lgl->trail);
		if (lgl->next2 < trail) {
			lit = lglpeek(&lgl->trail, lgl->next2++);
			lglprop2(lgl, lit);
			continue; // TODO try to optionally uncomment
		}
		if (lgl->next >= trail)
			break;
		count++;
		lit = lglpeek(&lgl->trail, lgl->next++);
		lglprop(lgl, lit);
	}
	if (lgl->lkhd)
		ADDSTEPS(props.lkhd, count);
	else if (lgl->simp)
		ADDSTEPS(props.simp, count);
	else
		ADDSTEPS(props.search, count);
	assert(lglhasconflict(lgl) || lglbcpcomplete(lgl));
	return !lglhasconflict(lgl);
}

static int
lglbcpsearch(LGL* lgl)
{
	int lit, count = 0;
	assert(!lgl->simp);
	assert(!lgl->notfullyconnected);
	while ((!lgl->failed || !lgl->level) && !lgl->conf.lit && lgl->next < (int)lglcntstk(&lgl->trail)) {
		lit = lglpeek(&lgl->trail, lgl->next++);
		lglpropsearch(lgl, lit);
		count++;
	}
	ADDSTEPS(props.search, count);
	lgl->next2 = lgl->next;
	if (lgl->conf.lit && lgl->failed) {
		LOG(2, "inconsistency overwrites failed assumption %d", lgl->failed);
		lgl->failed = 0;
	}
	assert(lglhasconflict(lgl) || lgl->failed || lglbcpcomplete(lgl));
	return !lgl->conf.lit && !lgl->failed;
}

#ifndef NDEBUG
static void
lglchkclnvar(LGL* lgl)
{
	AVar* av;
	int i;
	for (i = 2; i < lgl->nvars; i++) {
		av = lglavar(lgl, i);
		assert(!av->mark);
	}
}
#endif

#ifdef RESOLVENT
static int
lglmaintainresolvent(LGL* lgl)
{
#ifndef NDEBUG
	if (lgl->opts->check.val >= 1)
		return 1;
#endif
#ifndef NLGLOG
	if (lgl->opts->log.val >= 2)
		return 1;
#endif
	return 0;
}
#endif

static int
lgldecision(LGL* lgl, int lit)
{
	int* rsn = lglrsn(lgl, lit);
	int tag = rsn[0] & MASKCS;
	return tag == DECISION;
}

static int
lglassumption(LGL* lgl, int lit)
{
	return lglavar(lgl, lit)->assumed;
}

static int
lglpull(LGL* lgl, int lit)
{
	AVar* av = lglavar(lgl, lit);
	int level, res;
	level = lglevel(lgl, lit);
	if (!level)
		return 0;
	if (av->mark)
		return 0;
	av->mark = 1;
	lglpushstk(lgl, &lgl->seen, lit);
#ifdef RESOLVENT
	if (lglmaintainresolvent(lgl)) {
		lglpushstk(lgl, &lgl->resolvent, lit);
		LOG(2, "adding %d to resolvent", lit);
		LOGRESOLVENT(3, "resolvent after adding %d is", lit);
	}
#endif
	if (level == lgl->level) {
		LOG(2, "reason literal %d at same level %d", lit, lgl->level);
		res = 1;
	} else {
		lglpushstk(lgl, &lgl->clause, lit);
		LOG(2, "adding literal %d at upper level %d to 1st UIP clause", lit, lglevel(lgl, lit));
		lgluselevel(lgl, level);
		res = 0;
	}
	return res;
}

static int
lglusedtwice(LGL* lgl, int lit)
{
	int level;
	if (!lgl->opts->usedtwice.val)
		return 1;
	level = lglevel(lgl, lit);
	lgl->stats->mincls.usedtwice.search++;
	if (lglevelused(lgl, level) >= 2)
		return 1;
	lgl->stats->mincls.usedtwice.hits++;
	return 0;
}

static int
lglpoison(LGL* lgl, int lit, Stk* stk, int local)
{
	AVar* av = lglavar(lgl, lit);
	int level, res;
	if (av->mark)
		res = 0;
	else {
		level = lglevel(lgl, lit);
		if (!level)
			res = 0;
		else {
			assert(level < lgl->level);
			if (lgldecision(lgl, lit))
				res = 1;
			else if (lglevelused(lgl, level) < 2)
				res = 1;
			else {
				if (lgl->opts->poison.val)
					lgl->stats->mincls.poison.search++;
				if (av->poisoned) {
					assert(lgl->opts->poison.val);
					lgl->stats->mincls.poison.hits++;
					res = 1;
				} else if (local)
					res = 1;
				else if (lglusedtwice(lgl, lit)) {
					av->mark = 1;
					lglpushstk(lgl, &lgl->seen, lit);
					lglpushstk(lgl, stk, lit);
					res = 0;
				} else
					res = 1;
			}
		}
	}
	if (res && lgl->opts->poison.val && !av->poisoned) {
		av->poisoned = 1;
		lglpushstk(lgl, &lgl->poisoned, lit);
	}
	return res;
}

static int
lglminclslit(LGL* lgl, int start, int local)
{
	int lit, tag, r0, r1, other, *p, *q, *top, old, level;
	int poisoned, *rsn, found;
	AVar *av, *bv;
	assert(lglmarked(lgl, start));
	lit = start;
	level = lglevel(lgl, lit);
	assert(level > 0);
	if (level == lgl->level)
		return 0;
	if (!lglusedtwice(lgl, lit))
		return 0;
	rsn = lglrsn(lgl, lit);
	r0 = rsn[0];
	tag = (r0 & MASKCS);
	if (tag == DECISION)
		return 0;
	old = lglcntstk(&lgl->seen);
	assert(lglmtstk(&lgl->minstk));
	for (;;) {
		r1 = rsn[1];
		if (tag == BINCS || tag == TRNCS) {
			other = r0 >> RMSHFT;
			if (lglpoison(lgl, other, &lgl->minstk, local))
				goto FAILED;
			if (tag == TRNCS && lglpoison(lgl, r1, &lgl->minstk, local))
				goto FAILED;
		} else {
			assert(tag == LRGCS);
			p = lglidx2lits(lgl, (r0 & REDCS), r1);
			found = 0;
			while ((other = *p++)) {
				if (other == -lit)
					found++;
				else if (lglpoison(lgl, other, &lgl->minstk, local))
					goto FAILED;
			}
			assert(found == 1);
		}
		if (lglmtstk(&lgl->minstk)) {
			lglrelstk(lgl, &lgl->minstk);
			return 1;
		}
		lit = lglpopstk(&lgl->minstk);
		assert(lglavar(lgl, lit)->mark);
		rsn = lglrsn(lgl, lit);
		r0 = rsn[0];
		tag = (r0 & MASKCS);
	}
FAILED:
	lglclnstk(&lgl->minstk);
	p = lgl->seen.top;
	top = lgl->seen.top = lgl->seen.start + old;
	while (p > top) {
		lit = *--p;
		av = lglavar(lgl, lit);
		assert(av->mark);
		av->mark = 0;
		if (!lgl->opts->poison.val)
			continue;
		poisoned = av->poisoned;
		if (poisoned)
			continue;
		rsn = lglrsn(lgl, lit);
		r0 = rsn[0];
		tag = (r0 & MASKCS);
		assert(tag != DECISION);
		r1 = rsn[1];
		if (tag == BINCS || tag == TRNCS) {
			other = r0 >> RMSHFT;
			bv = lglavar(lgl, other);
			if (bv->poisoned)
				poisoned = 1;
			else if (tag == TRNCS) {
				bv = lglavar(lgl, r1);
				if (bv->poisoned)
					poisoned = 1;
			}
		} else {
			assert(tag == LRGCS);
			q = lglidx2lits(lgl, (r0 & REDCS), r1);
			while (!poisoned && (other = *q++))
				poisoned = lglavar(lgl, other)->poisoned;
		}
		if (!poisoned)
			continue;
		assert(!av->poisoned);
		av->poisoned = 1;
		lglpushstk(lgl, &lgl->poisoned, lit);
	}
	return 0;
}

double
lglmaxmb(LGL* lgl)
{
	REQINIT();
	return (lgl->stats->bytes.max + sizeof *lgl) / (double)(1 << 20);
}

size_t
lglbytes(LGL* lgl)
{
	REQINIT();
	return lgl->stats->bytes.current;
}

double
lglmb(LGL* lgl)
{
	REQINIT();
	return (lgl->stats->bytes.current + sizeof *lgl) / (double)(1 << 20);
}

static double
lglavg(double n, double d)
{
	return d != 0 ? n / d : 0.0;
}

static double
lglheight(LGL* lgl)
{
	return lgl->stats->jlevel.slow.val / (double)(1ull << 32);
}

static double
lgltlevel(LGL* lgl)
{
	return lgl->stats->tlevel.val / (double)(1ull << 32);
}

static double
lglglue(LGL* lgl)
{
	return lgl->stats->glue.slow.val / (double)(1ull << 32);
}

static void
lglrephead(LGL* lgl)
{
	if (lgl->tid > 0)
		return;
	lgl->repforcehead = 0;
	lgl->repcntdown = REPMOD;
	if (lgl->tid > 0)
		return;
	if (lgl->cbs && lgl->cbs->msglock.lock)
		lgl->cbs->msglock.lock(lgl->cbs->msglock.state);
	assert(lgl->prefix);
	fprintf(lgl->out, "%s\n", lgl->prefix);
	fprintf(lgl->out,
			"%s%s"
			" seconds         irredundant            redundant clauses   glue"
			"   iterations\"      MB    stability\n",
			lgl->prefix,
			!lgl->tid ? "  " : "");
	fprintf(lgl->out,
			"%s%s"
			"         variables clauses conflicts  large ternary binary "
			"    jlevel       jlevel'  agility     tlevel\n",
			lgl->prefix,
			!lgl->tid ? "  " : "");
	fprintf(lgl->out, "%s\n", lgl->prefix);
	fflush(lgl->out);
	if (lgl->cbs && lgl->cbs->msglock.unlock)
		lgl->cbs->msglock.unlock(lgl->cbs->msglock.state);
}

#ifndef NLGLOG
static double
lglitavg(LGL* lgl)
{
	return lgl->stats->its.avg.slow.val / (double)(1ull << 32);
}
#endif

static double
lglitder(LGL* lgl)
{
	return 10000.0 * lglavg(lgl->stats->its.avg.diff.smoothed.val / (double)(1ull << 32), lgl->stats->confs);
}

static double
lgljlevelder(LGL* lgl)
{
	return lgl->stats->jlevel.diff.smoothed.val / (double)(1ll << 32);
}

static void
lglrep(LGL* lgl, int level, char type)
{
	if (lgl->opts->verbose.val < level)
		return;
	if ((level > 0 && lgl->repforcehead) || !lgl->repcntdown--)
		lglrephead(lgl);
	lglprt(lgl,
		   level,
		   " %c"	// type
		   " %6.1f" // time
		   " %7d"	// rem
		   " %8d"	// irr
		   " %9lld" // confs
		   " %7d"	// lrg
		   " %6d"	// trn
		   " %5d"	// bin
		   " %4.0f" // glue
		   " %5.0f" // height
		   " %6.0f" // it"
		   " %5.0f" // jl'
		   " %4.0f" // mb
		   " %3.0f" // agility
		   " %5.0f" // stability
		   " %6.0f" // trail
		   ,
		   type,
		   lgl->opts->abstime.val ? lglgetime(lgl) : lglsec(lgl),
		   lglrem(lgl),
		   lgl->stats->irr.clauses.cur,
		   (LGLL)lgl->stats->confs,
		   lgl->stats->red.lrg,
		   lgl->stats->red.trn,
		   lgl->stats->red.bin,
		   lglglue(lgl),
		   lglheight(lgl),
		   lglitder(lgl),
		   lgljlevelder(lgl),
		   lglmb(lgl),
		   100.0 * (lgl->stats->agility / (double)(1ll << 32)),
		   1000.0 * (lgl->stats->stability.avg.val / (double)(1ll << 32)),
		   lgltlevel(lgl));
	lgl->stats->reported++;
}

static void
lglflshrep(LGL* lgl)
{
	if (!lgl->stats->reported)
		return;
	if (lgl->stats->reported % REPMOD)
		lglrephead(lgl);
	else
		lglprt(lgl, 1, "");
}

static void
lglfitlir(LGL* lgl, Stk* lir)
{
	lglfitstk(lgl, lir);
}

static void
lglchkred(LGL* lgl)
{
#ifndef NDEBUG
	int glue, idx, sign, lit, thisum, sum, sum2, sum3;
	int blit, tag, red, other, other2;
	int *p, *c, *w, *eow;
	HTS* hts;
	Stk* lir;
	if (lgl->mt)
		return;
	sum = 0;
	for (glue = 0; glue < MAXGLUE; glue++) {
		lir = lgl->red + glue;
		thisum = 0;
		for (c = lir->start; c < lir->top; c = p + 1) {
			p = c;
			if (*p >= NOTALIT)
				continue;
			while (*p)
				p++;
			assert(p - c >= 4);
			thisum++;
		}
		assert(thisum == lgl->stats->lir[glue].clauses);
		sum += thisum;
	}
	assert(sum == lgl->stats->red.lrg);
	sum2 = sum3 = 0;
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				red = blit & REDCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (!red || tag == LRGCS || tag == OCCS)
					continue;
				other = blit >> RMSHFT;
				if (abs(other) < idx)
					continue;
				if (tag == BINCS)
					sum2++;
				else {
					assert(tag == TRNCS);
					other2 = *p;
					if (abs(other2) < idx)
						continue;
					sum3++;
				}
			}
		}
	assert(sum2 == lgl->stats->red.bin);
	assert(sum3 == lgl->stats->red.trn);
#endif
	(void)lgl;
}

static void
lglinitredl(LGL* lgl)
{
	lgl->limits->reduce.redlarge = lgl->opts->reduceinit.val;
	lglprt(lgl,
		   2,
		   "[set-reduce-limit] initial reduce limit of %lld after %lld conflicts",
		   (LGLL)lgl->limits->reduce.redlarge,
		   (LGLL)lgl->stats->confs);
}

static int
lglmemout(LGL* lgl)
{
	size_t cur;
	int res;
	if (lgl->opts->memlim.val < 0)
		return 0;
	cur = lgl->stats->bytes.current;
	cur >>= 20;
	res = (cur >= (size_t)lgl->opts->memlim.val);
	if (res)
		lglprt(lgl, 2, "memory limit of %d MB hit after allocating %lld MB", lgl->opts->memlim.val, (LGLL)cur);
	return res;
}

static Val
lgliphase(LGL* lgl, int lit)
{
	Val res = lglavar(lgl, lit)->phase;
	if (lit < 0)
		res = -res;
	return res;
}

static void
lglretire(LGL* lgl)
{
	int count, ready, inactive, glue, *p, *c, act;
	const int retireint = lgl->opts->retireint.val;
	Stk* s;
	assert(lgl->opts->retirenb.val);
	count = ready = inactive = 0;
	for (glue = 0; glue < MAXGLUE; glue++) {
		s = lgl->red + glue;
		for (c = s->start; c < s->top; c = p + 1) {
			act = *(p = c);
			if (act <= NOTALIT + retireint) {
				count++;
				inactive++;
				assert(lglisact(act));
				if (act <= NOTALIT + 1) {
					act = NOTALIT;
					ready++;
				} else
					act--;
				assert(lglisact(act));
				*p++ = act;
			} else if (act < REMOVED) {
				count++;
				act = NOTALIT + retireint + (act - NOTALIT - retireint) / 2;
				assert(lglisact(act));
				*p++ = act;
			}
			while (*p)
				p++;
		}
	}
	lglprt(lgl,
		   2,
		   "[reduce-%d] %d ready to retire %.0f%%, %d inactive %.0f%%",
		   lgl->stats->reduced.count,
		   ready,
		   lglpcnt(ready, count),
		   inactive,
		   lglpcnt(inactive, count));
}

static int
lglcmpagsl(LGL* lgl, PAGSL* a, PAGSL* b)
{
	int res;
	if ((res = b->psm - a->psm))
		return res;
	if ((res = b->glue - a->glue))
		return res;
	if ((res = a->act - b->act))
		return res;
	if ((res = b->size - a->size))
		return res;
	if ((res = a->lidx - b->lidx))
		return res;
	(void)lgl;
	return 0;
}

#define LGLCMPAGSL(A, B) lglcmpagsl(lgl, (A), (B))

static void
lglreduce(LGL* lgl, int forced)
{
	int *p, *q, *start, *c, **maps, *sizes, *map, *eow, *rsn;
	int nlocked, collected, sumcollected, nunlocked, moved, act, psm;
	int glue, target, nkeep, retired, sumretired;
	PAGSL *pagsls, *pagsl;
	int npagsls, szpagsls;
	const int retirenb = lgl->opts->retirenb.val;
	const int retiremin = lgl->opts->retiremin.val;
	int size, idx, tag, red, i, blit;
	int r0, lidx, src, dst, lit;
	int inc, delta, npromoted;
	char type = '-';
	HTS* hts;
	DVar* dv;
	Stk* lir;
	lglchkred(lgl);
	lglstart(lgl, &lgl->times->reduce);
	lgl->stats->reduced.count++;
	if (retirenb)
		lglretire(lgl);
	LOG(1, "starting reduction %d", lgl->stats->reduced.count);
	delta = lgl->stats->red.lrg;
	delta -= lgl->lrgluereasons;
	delta -= lgl->stats->lir[0].clauses;
	assert(delta >= 0);
	if (delta > 3 * lgl->limits->reduce.redlarge / 2)
		target = delta - lgl->limits->reduce.redlarge / 2;
	else
		target = delta / 2;
	if (target < 0)
		target = 0;
	lglprt(lgl,
		   2,
		   "[reduce-%d] target is to collect %d clauses %.0f%%",
		   lgl->stats->reduced.count,
		   target,
		   lglpcnt(target, lgl->stats->red.lrg));
	NEW(maps, MAXGLUE);
	NEW(sizes, MAXGLUE);
	for (glue = 0; glue < MAXGLUE; glue++) {
		lir = lgl->red + glue;
		size = lglcntstk(lir);
		assert(!size || size >= 6);
		size = (size + 5) / 6;
		sizes[glue] = size;
		lglfitstk(lgl, lir);
		NEW(maps[glue], size);
		map = maps[glue];
		for (i = 0; i < size; i++)
			map[i] = -2;
	}
	nlocked = 0;
	for (i = 0; i < (int)lglcntstk(&lgl->trail); i++) {
		lit = lglpeek(&lgl->trail, i);
		idx = abs(lit);
		rsn = lglrsn(lgl, idx);
		r0 = rsn[0];
		red = r0 & REDCS;
		if (!red)
			continue;
		tag = r0 & MASKCS;
		if (tag != LRGCS)
			continue;
		lidx = rsn[1];
		glue = lidx & GLUEMASK;
		if (glue == MAXGLUE)
			continue;
		lidx >>= GLUESHFT;
#ifndef NLGLOG
		lir = lgl->red + glue;
		assert(lglpeek(lir, lidx) < REMOVED);
		LOGCLS(5, lir->start + lidx, "locking reason of literal %d glue %d clause", lit, glue);
#endif
		lidx /= 6;
		assert(maps);
		assert(maps[glue]);
		assert(lidx < sizes[glue]);
		assert(maps[glue][lidx] == -2);
		maps[glue][lidx] = -1;
		nlocked++;
	}
	lglprt(lgl,
		   2,
		   "[reduce-%d] locked %d learned reason clauses %.0f%%",
		   lgl->stats->reduced.count,
		   nlocked,
		   lglpcnt(nlocked, lgl->stats->red.lrg));
	npromoted = 0;
	while (!lglmtstk(&lgl->promote)) {
		lidx = lglpopstk(&lgl->promote);
		glue = lidx & GLUEMASK;
		assert(glue < MAXGLUE);
		if (!maps[glue]) {
			assert(!sizes[glue]);
			continue;
		}
		assert(0 < glue), assert(glue < MAXGLUE);
		lidx >>= GLUESHFT;
		assert(lidx > 0);
		lir = lgl->red + glue;
		p = lir->start + lidx;
		if (p >= lir->top)
			continue;
		if (*p >= NOTALIT)
			continue;
		if (!lglisact(p[-1]))
			continue;
		if (maps[glue][lidx / 6] == -1)
			continue;
#ifndef NLGLOG
		LOGCLS(5, lir->start + lidx, "locking promoted glue %d clause", glue);
#endif
		assert(lidx / 6 < sizes[glue]);
		maps[glue][lidx / 6] = -1;
		lgl->stats->promote.locked++;
		npromoted++;
	}
	lglprt(lgl,
		   2,
		   "[reduce-%d] locked %d promoted learned clauses %.0f%%",
		   lgl->stats->reduced.count,
		   npromoted,
		   lglpcnt(npromoted, lgl->stats->red.lrg));

	szpagsls = lgl->stats->red.lrg;
	NEW(pagsls, szpagsls);
	retired = npagsls = 0;
	for (glue = 0; glue < MAXGLUE; glue++) {
		lir = lgl->red + glue;
		start = lir->start;
		for (c = start; c < lir->top; c = p + 1) {
			if ((act = *c++) == REMOVED) {
				for (p = c; p < lir->top && *p == REMOVED; p++)
					;
				p--;
				continue;
			}
			LGLCHKACT(act);
			lidx = c - start;
			act -= NOTALIT;
			psm = 0;
			for (p = c; (lit = *p); p++)
				if (lgliphase(lgl, lit) >= 0)
					psm++;
			size = p - c;
			LOG(5, "clause red[%d][%d] has: psm %d, act %d, size %d", glue, lidx, psm, act, size);

			if (maps[glue][lidx / 6] == -1)
				continue;
			assert(maps[glue][lidx / 6] == -2);

			if (retirenb && !act && glue >= retiremin) {
				retired++;
				maps[glue][lidx / 6] = -3;
				LOG(5, "retiring this inactive clause");
				continue;
			}

			if (!glue && size <= lgl->opts->gluekeepsize.val) {
				LOG(5, "keeping this glue 0 size %d clause", size);
				maps[glue][lidx / 6] = -1;
				continue;
			}

			assert(npagsls < szpagsls);
			pagsl = pagsls + npagsls++;
			pagsl->psm = psm;
			pagsl->act = act;
			pagsl->glue = glue;
			pagsl->size = size;
			pagsl->lidx = lidx;
		}
	}
	assert(npagsls <= szpagsls);
	LOG(2, "retiring %d learned clauses %.0f%%", retired, lglpcnt(retired, lgl->stats->red.lrg));

	SORT(PAGSL, pagsls, npagsls, LGLCMPAGSL);
	LOG(1, "copied and sorted %d activities", npagsls);

	nkeep = 0;
	for (idx = target; idx < npagsls; idx++) {
		pagsl = pagsls + idx;
		lidx = pagsl->lidx;
		glue = pagsl->glue;
		assert(lidx / 6 < sizes[glue]);
		maps[glue][lidx / 6] = -1;
		nkeep++;
		LOG(5,
			"marked clause red[%d][%d] to keep with psm %d, act %d, size %d",
			glue,
			lidx,
			pagsl->psm,
			pagsl->act,
			pagsl->size);
	}
	DEL(pagsls, szpagsls);
	LOG(1, "explicity marked %d additional clauses to keep", nkeep);
	sumcollected = sumretired = 0;
	for (glue = 0; glue < MAXGLUE; glue++) {
		lir = lgl->red + glue;
		map = maps[glue];
#ifndef NDEBUG
		size = sizes[glue];
#endif
		q = start = lir->start;
		collected = retired = 0;
		for (c = start; c < lir->top; c = p + 1) {
			act = *c++;
			if (act == REMOVED) {
				for (p = c; p < lir->top && *p == REMOVED; p++)
					;
				assert(p >= lir->top || *p < NOTALIT || lglisact(*p));
				p--;
				continue;
			}
			p = c;
			LGLCHKACT(act);
			src = (c - start) / 6;
			assert(src < size);
			if (map[src] <= -2) {
				assert(collected + retired < lgl->stats->lir[glue].clauses);
				if (map[src] == -2) {
					collected++;
					LOGCLS(5, c, "collecting glue %d clause", glue);
				} else {
					assert(map[src] == -3);
					retired++;
					LOGCLS(5, c, "retiring glue %d clause", glue);
				}
				lgldrupligdelclsaux(lgl, c);
				while (*p)
					p++;
			} else {
				dst = q - start + 1;
				map[src] = dst;
				if (p == q) {
					while (*p)
						p++;
					q = p + 1;
				} else {
					*q++ = act;
					LOGCLS(5, c, "moving from %d to %d glue %d clause", (c - start), dst, glue);
					while (*p)
						*q++ = *p++;
					*q++ = 0;
				}
			}
		}
		LOG(2, "collected %d glue %d clauses", collected, glue);
		assert(sumcollected + sumretired <= lgl->stats->red.lrg);
		assert(lgl->stats->lir[glue].clauses >= collected + retired);
		lgl->stats->lir[glue].clauses -= collected + retired;
		lgl->stats->lir[glue].reduced += collected;
		lgl->stats->lir[glue].retired += retired;
		sumcollected += collected;
		sumretired += retired;
		lir->top = q;
		lglfitlir(lgl, lir);
	}
	lglprt(lgl,
		   2,
		   "[reduce-%d] collected %d clauses %.0f%%, retired %d clauses %.0f%%",
		   lgl->stats->reduced.count,
		   sumcollected,
		   lglpcnt(sumcollected, lgl->stats->red.lrg),
		   sumretired,
		   lglpcnt(sumretired, lgl->stats->red.lrg));
	assert(sumcollected + sumretired <= lgl->stats->red.lrg);
	lgl->stats->red.lrg -= sumcollected + sumretired;
	lgl->stats->reduced.collected += sumcollected;
	lgl->stats->reduced.retired += sumretired;
	nunlocked = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglval(lgl, idx))
			continue;
		rsn = lglrsn(lgl, idx);
		r0 = rsn[0];
		red = r0 & REDCS;
		if (!red)
			continue;
		tag = r0 & MASKCS;
		if (tag != LRGCS)
			continue;
		lidx = rsn[1];
		glue = lidx & GLUEMASK;
		if (glue == MAXGLUE)
			continue;
		src = (lidx >> GLUESHFT);
		assert(src / 6 < sizes[glue]);
		dst = maps[glue][src / 6];
		assert(dst >= 0);
		dst <<= GLUESHFT;
		dst |= lidx & GLUEMASK;
		rsn[1] = dst;
		nunlocked++;
	}
	LOG(2, "unlocked %d reasons", nunlocked);
	assert(nlocked == nunlocked);
	collected = moved = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		dv = lgldvar(lgl, idx);
		for (i = 0; i <= 1; i++) {
			hts = dv->hts + i;
			if (!hts->offset)
				continue;
			q = lglhts2wchs(lgl, hts);
			eow = q + hts->count;
			for (p = q; p < eow; p++) {
				blit = *p;
				red = blit & REDCS;
				tag = blit & MASKCS;
				if (red && tag == LRGCS) {
					lidx = *++p;
					glue = lidx & GLUEMASK;
					if (glue == MAXGLUE) {
						dst = lidx >> GLUESHFT;
					} else {
						src = lidx >> GLUESHFT;
						assert(src / 6 < sizes[glue]);
						dst = maps[glue][src / 6];
					}
					if (dst >= 0) {
						moved++;
						*q++ = blit;
						*q++ = (dst << GLUESHFT) | (lidx & GLUEMASK);
					} else
						collected++;
				} else {
					*q++ = blit;
					if (tag != BINCS) {
						assert(tag == TRNCS || tag == LRGCS);
						*q++ = *++p;
					}
				}
			}
			lglshrinkhts(lgl, hts, hts->count - (p - q));
		}
	}
	LOG(1, "moved %d and collected %d occurrences", moved, collected);
	for (glue = 0; glue < MAXGLUE; glue++)
		DEL(maps[glue], sizes[glue]);
	DEL(sizes, MAXGLUE);
	DEL(maps, MAXGLUE);
	if (lgl->opts->reducefixed.val)
		goto NOINC;
	if (!lgl->limits->reduce.reset)
		lgl->limits->reduce.reset = 1;
	if (lgl->stats->reduced.count >= lgl->limits->reduce.reset)
		lgl->limits->reduce.reset <<= 1;
	if ((lgl->opts->reducereset.val == 1 && lglispow2(lgl->stats->reduced.count)) ||
		(lgl->opts->reducereset.val == 2 && lglispow2(lgl->stats->reduced.count & ((lgl->stats->reduced.count - 1))))) {
		lgl->limits->reduce.redlarge = 0;
		inc = lgl->opts->reduceinc.val;
		lgl->stats->reduced.reset++;
		lglprt(lgl, 2, "[reduce-%d] reset", lgl->stats->reduced.count);
	} else if (lglmemout(lgl)) {
		inc = 0;
		lglprt(lgl, 2, "[reduce-%d] no increase of reduce limit since memory limit was hit", lgl->stats->reduced.count);
		lgl->stats->reduced.memlim++;
	} else {
		inc = lgl->opts->reduceinc.val;
		lglprt(lgl,
			   2,
			   "[reduce-%d] increase of reduce limit by %d to %d",
			   lgl->stats->reduced.count,
			   inc,
			   lgl->limits->reduce.redlarge + inc);
		lgl->stats->reduced.arith++;
	}
	LOG(1, "reduce increment %d", inc);
	lgl->limits->reduce.redlarge += inc;
	assert(forced || lgl->opts->reduce.val);
NOINC:
	lglrelstk(lgl, &lgl->learned);
	lglrep(lgl, 1, type);
	lglchkred(lgl);
	lglstop(lgl);
#ifndef NDEBUG
	lglclnstk(&lgl->prevclause);
	lgl->prevglue = -1;
#else
	(void)forced;
#endif
}

void
lglreducecache(LGL* lgl)
{
	REQINITNOTFORKED();
	TRAPI("reduce");
	if (lgl->mt)
		return;
	lglinitredl(lgl);
	lglreduce(lgl, 1);
	lglinitredl(lgl);
	lglprt(lgl, 1, "[reduce-cache] new limit %d", lgl->limits->reduce.redlarge);
	if (lgl->clone)
		lglreducecache(lgl->clone);
}

static void
lgliflushcache(LGL* lgl, int keep)
{
	int idx, sign, lit, blit, tag, red, *w, *q, lidx, glue;
	const int druplig = lgl->opts->druplig.val;
	int bin, trn, lrg, start;
	const int *p, *eow;
	HTS* hts;
	assert(1 <= keep), assert(keep <= 4);
	if (lgl->level)
		lglbacktrack(lgl, 0);
	bin = trn = lrg = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			q = w;
			for (p = q; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				red = blit & REDCS;
				if (red) {
					if (tag == BINCS) {
						if (keep < 2) {
							if (druplig) {
								int other = blit >> RMSHFT;
								if (idx < abs(other))
									lgldrupligdelclsarg(lgl, lit, other, 0);
							}
							continue;
						}
					} else if (tag == TRNCS) {
						if (keep < 3) {
							if (druplig) {
								int other = blit >> RMSHFT;
								if (idx < abs(other)) {
									int other2 = *p;
									if (idx < abs(other2))
										lgldrupligdelclsarg(lgl, lit, other, other2, 0);
								}
							}
							continue;
						}
					} else {
						assert(tag == LRGCS);
						lidx = *p;
						glue = lidx & GLUEMASK;
						if (keep < 4 || glue > 0) {
							if (druplig) {
								const int* c = lglidx2lits(lgl, red, lidx);
								if (*c == lit)
									lgldrupligdelclsaux(lgl, c);
							}
							continue;
						}
					}
				}
				*q++ = blit;
				if (tag == TRNCS || tag == LRGCS)
					*q++ = *p;
			}
			lglshrinkhts(lgl, hts, q - w);
		}
	}
	if (keep < 2)
		bin = lgl->stats->red.bin, lgl->stats->red.bin = 0;
	if (keep < 3)
		trn = lgl->stats->red.trn, lgl->stats->red.trn = 0;
	start = (keep >= 4);
	for (glue = start; glue < MAXGLUE; glue++) {
		lrg += lgl->stats->lir[glue].clauses;
		lgl->stats->lir[glue].clauses = 0;
	}
	assert(lrg <= lgl->stats->red.lrg), lgl->stats->red.lrg -= lrg;
	lglrelstk(lgl, &lgl->learned);
	for (glue = start; glue < MAXGLUE; glue++)
		lglrelstk(lgl, lgl->red + glue);
	lglprt(lgl, 1, "[flush-cache] %d binary, %d ternary, %d large", bin, trn, lrg);
#ifndef NDEBUG
	lglclnstk(&lgl->prevclause);
	lgl->prevglue = -1;
#endif
}

void
lglflushcache(LGL* lgl)
{
	REQINITNOTFORKED();
	TRAPI("flush");
	if (lgl->mt)
		return;
	lglinitredl(lgl);
	lgliflushcache(lgl, 3);
	lglinitscores(lgl);
	lglinitredl(lgl);
	lglprt(lgl, 1, "[flush-cache] new limit %d", lgl->limits->reduce.redlarge);
	if (lgl->clone)
		lglflushcache(lgl->clone);
}

static void
lglrmbwch(LGL* lgl, int lit, int other, int red)
{
	int *p, blit, blit1, *w, *eow, tag;
	HTS* hts;
	assert(!red || red == REDCS);
	LOG(3, "removing %s binary watch %d blit %d", lglred2str(red), lit, other);
	hts = lglhts(lgl, lit);
	assert(hts->count >= 1);
	p = w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	lglrminc(lgl, w, eow);
	blit1 = RMSHFTLIT(other) | red | BINCS;
	for (;;) {
		assert(p < eow);
		blit = *p++;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS) {
			p++;
			continue;
		}
		if (tag == OCCS) {
			assert(lgl->dense);
			continue;
		}
		assert(tag == BINCS);
		if (blit == blit1)
			break;
	}
	while (p < eow)
		p[-1] = p[0], p++;
	lglshrinkhts(lgl, hts, p - w - 1);
}

static int
lglpopesched(LGL* lgl)
{
	Stk* s = &lgl->esched;
	int res, last, cnt, *p;
	EVar* ev;
	assert(!lglmtstk(s));
	res = *s->start;
	assert(!lglifrozen(lgl, res));
	LOGESCHED(4, res, "popped");
	ev = lglevar(lgl, res);
	assert(!ev->pos);
	ev->pos = -1;
	last = lglpopstk(s);
	cnt = lglcntstk(s);
	if (!cnt) {
		assert(last == res);
		return res;
	}
	p = lglepos(lgl, last);
	assert(*p == cnt);
	*p = 0;
	*s->start = last;
	lgledown(lgl, last);
	return res;
}

static void
lgldecocc(LGL* lgl, int lit)
{
	int idx, sign, change;
	EVar* ev;
	if (!lgl->occs)
		return;
	idx = abs(lit), sign = (lit < 0);
	ev = lglevar(lgl, lit);
	assert(ev->occ[sign] > 0);
	ev->occ[sign] -= 1;
	if (!lglisfree(lgl, lit))
		return;
	change = lglecalc(lgl, ev);
	LOG(3, "dec occ of %d now occs[%d] = %d %d", lit, idx, ev->occ[0], ev->occ[1]);
	if (ev->pos < 0)
		lglesched(lgl, idx);
	else if (change < 0)
		lgleup(lgl, idx);
	else if (change > 0)
		lgledown(lgl, idx);
}

static void
lglrmbcls(LGL* lgl, int a, int b, int red)
{
	lglrmbwch(lgl, a, b, red);
	lglrmbwch(lgl, b, a, red);
	LOG(2, "removed %s binary clause %d %d", lglred2str(red), a, b);
	lgldeclscnt(lgl, 2, red, 0);
	if (!red && lgl->dense)
		lgldecocc(lgl, a), lgldecocc(lgl, b);
}

static void
lglrmtcls(LGL* lgl, int a, int b, int c, int red)
{
	lglrmtwch(lgl, a, b, c, red);
	lglrmtwch(lgl, b, a, c, red);
	lglrmtwch(lgl, c, a, b, red);
	LOG(2, "removed %s ternary clause %d %d %d", lglred2str(red), a, b, c);
	lgldeclscnt(lgl, 3, red, 0);
	if (!red && lgl->dense)
		lgldecocc(lgl, a), lgldecocc(lgl, b), lgldecocc(lgl, c);
}

static void
lgltrimlitstk(LGL* lgl, int red, int lidx)
{
	Stk* s;
	int* p;
	if (lgl->notrim)
		return;
	s = lglidx2stk(lgl, red, lidx);
	p = s->top;
	while (p > s->start && p[-1] == REMOVED)
		p--;
	if (p < s->top) {
		int64_t trimmed = s->top - p;
#ifndef NLGLOG
		if (red)
			LOG(4, "trimmed 'red[%d]' by %lld", (lidx & GLUEMASK), (LGLL)trimmed);
		else
			LOG(4, "trimmed 'irr' by %lld", (LGLL)trimmed);
#endif
		lgl->stats->trims += trimmed;
		s->top = p;
		if (red) {
			int glue = (lidx & GLUEMASK), maxlidx;
			assert(s == &lgl->red[glue]);
			maxlidx = (lglcntstk(s) << GLUESHFT) | glue;
			for (p = lgl->learned.start; p < lgl->learned.top; p += 3) {
				int olidx = *p, oglue = (olidx & GLUEMASK);
				if (oglue == glue && olidx >= maxlidx)
					*p = INT_MIN;
			}
		}
	}
}

static void
lglrmlcls(LGL* lgl, int lidx, int red)
{
	int *c, *p, glue, lit;
	assert(!red || red == REDCS);
	glue = red ? (lidx & GLUEMASK) : 0;
	c = lglidx2lits(lgl, red, lidx);
	if (!red || glue < MAXGLUE) {
		lglrmlwch(lgl, c[0], red, lidx);
		lglrmlwch(lgl, c[1], red, lidx);
	}
	if (!red && lgl->dense) {
		for (p = c; (lit = *p); p++) {
			lglrmlocc(lgl, lit, red, lidx);
			lgldecocc(lgl, lit);
		}
	}
	if (red && glue < MAXGLUE) {
		LGLCHKACT(c[-1]);
		c[-1] = REMOVED;
	}
	for (p = c; *p; p++)
		*p = REMOVED;
	*p = REMOVED;
	if (glue != MAXGLUE)
		lgldeclscnt(lgl, p - c, red, glue);
	lgltrimlitstk(lgl, red, lidx);
}

static void
lgldynsub(LGL* lgl, int lit, int r0, int r1)
{
	int red, tag, other, other2;
	const int* c;
	tag = r0 & MASKCS;
	LOGREASON(2, lit, r0, r1, "removing on-the-fly subsumed");
	red = r0 & REDCS;
	if (red)
		lgl->stats->otfs.sub.red++;
	else
		lgl->stats->otfs.sub.irr++;
	if (tag == BINCS) {
		lgl->stats->otfs.sub.bin++;
		other = (r0 >> RMSHFT);
		lgldrupligdelclsarg(lgl, lit, other, 0);
		lglrmbcls(lgl, lit, other, red);
	} else if (tag == TRNCS) {
		lgl->stats->otfs.sub.trn++;
		other = (r0 >> RMSHFT), other2 = r1;
		lgldrupligdelclsarg(lgl, lit, other, other2, 0);
		lglrmtcls(lgl, lit, other, other2, red);
	} else {
		assert(tag == LRGCS);
		lgl->stats->otfs.sub.lrg++;
		c = lglidx2lits(lgl, red, r1);
		lgldrupligdelclsaux(lgl, c);
		lglrmlcls(lgl, r1, red);
	}
}

static void
lglunflict(LGL* lgl, int lit)
{
	lgl->conf.lit = lit;
	lgl->conf.rsn[0] = RMSHFTLIT(lit) | UNITCS;
	LOG(1, "inconsistent unary clause %d", lit);
}

static void
lgldynstr(LGL* lgl, int del, int lit, int r0, int r1)
{
	int *p, *c, lidx, other, red, tag, glue, other2, other3, blit;
	tag = r0 & MASKCS;
	LOGREASON(2, lit, r0, r1, "on-the-fly strengthening by removing %d from", del);
	red = r0 & REDCS;
	if (red)
		lgl->stats->otfs.str.red++;
	else
		lgl->stats->otfs.str.irr++;
	lgl->stats->otfs.total++;
	lgl->stats->prgss++;
	if (!red)
		lgl->stats->irrprgss++;
	if (tag == BINCS) {
		lgl->stats->otfs.str.bin++;
		other = (del == lit) ? (r0 >> RMSHFT) : lit;
		assert(other != del);
		lgldrupligaddclsarg(lgl, REDCS, other, 0);
		lgldrupligdelclsarg(lgl, del, other, 0);
		lglrmbcls(lgl, del, other, red);
		lglunflict(lgl, other);
		return;
	}
	if (tag == TRNCS) {
		lgl->stats->otfs.str.trn++;
		if (lit == del)
			other = (r0 >> RMSHFT), other2 = r1;
		else if (del == r1)
			other = lit, other2 = (r0 >> RMSHFT);
		else
			other = lit, other2 = r1;
		assert(del != other && del != other2);
		lgldrupligaddclsarg(lgl, REDCS, other, other2, 0);
		lgldrupligdelclsarg(lgl, del, other, other2, 0);
		lglrmtcls(lgl, del, other, other2, red);
		if (!red)
			lglincirr(lgl, 2);
		else
			lgl->stats->red.bin++, assert(lgl->stats->red.bin > 0);
		lglwchbin(lgl, other, other2, red);
		lglwchbin(lgl, other2, other, red);
		if (lglevel(lgl, other) < lglevel(lgl, other2))
			SWAP(int, other, other2);
		blit = RMSHFTLIT(other2) | BINCS | red;
		lglbonflict(lgl, other, blit);
		return;
	}
	assert(tag == LRGCS);
	lgl->stats->otfs.str.lrg++;
	lidx = r1;
	glue = red ? (lidx & GLUEMASK) : 0;
	c = lglidx2lits(lgl, red, lidx);
	for (p = c; *p != del; p++)
		assert(*p);
	if (glue < MAXGLUE) {
		lglrmlwch(lgl, c[0], red, lidx);
		lglrmlwch(lgl, c[1], red, lidx);
	}
	while ((other = *++p))
		p[-1] = other;
	p[-1] = 0, *p = REMOVED;
	lgltrimlitstk(lgl, red, lidx);
	if (!red)
		assert(lgl->stats->irr.lits.cur), lgl->stats->irr.lits.cur--;
	lglorderclsaux(lgl, c + 0);
	lglorderclsaux(lgl, c + 1);
	lgldrupligaddclsaux(lgl, REDCS, c);
	assert(p - c > 3);
	if (p - c == 4) {
		assert(!c[3] && c[4] >= NOTALIT);
		other = c[0], other2 = c[1], other3 = c[2];
		if (red && glue < MAXGLUE) {
			LGLCHKACT(c[-1]);
			c[-1] = REMOVED;
		}
		c[0] = c[1] = c[2] = c[3] = REMOVED;
		lgltrimlitstk(lgl, red, lidx);
		if (lglevel(lgl, other2) < lglevel(lgl, other3))
			SWAP(int, other2, other3);
		if (lglevel(lgl, other) < lglevel(lgl, other2))
			SWAP(int, other, other2);
		lglwchtrn(lgl, other, other2, other3, red);
		lglwchtrn(lgl, other2, other, other3, red);
		lglwchtrn(lgl, other3, other, other2, red);
		if (red) {
			if (glue < MAXGLUE) {
				assert(lgl->stats->red.lrg > 0);
				lgl->stats->red.lrg--;
				assert(lgl->stats->lir[glue].clauses > 0);
				lgl->stats->lir[glue].clauses--;
			}
			lgl->stats->red.trn++;
			assert(lgl->stats->red.trn > 0);
		}
		lgltonflict(lgl, other, RMSHFTLIT(other2) | red | TRNCS, other3);
	} else {
		if (glue < MAXGLUE) {
			LOG(3, "new head literal %d", c[0]);
			(void)lglwchlrg(lgl, c[0], c[1], red, lidx);
			LOG(3, "new tail literal %d", c[1]);
			(void)lglwchlrg(lgl, c[1], c[0], red, lidx);
		}
		lglonflict(lgl, 0, c[0], red, lidx);
	}
}

static void
lglclnframes(LGL* lgl)
{
	while (!lglmtstk(&lgl->frames))
		lglunuselevel(lgl, lglpopstk(&lgl->frames));
}

static void
lglclnpoisoned(LGL* lgl)
{
	while (!lglmtstk(&lgl->poisoned)) {
		int lit = lglpopstk(&lgl->poisoned);
		AVar* av = lglavar(lgl, lit);
		assert(!av->mark);
		assert(av->poisoned);
		av->poisoned = 0;
	}
}

static void
lglclnana(LGL* lgl)
{
#ifdef RESOLVENT
	if (lglmaintainresolvent(lgl))
		lglclnstk(&lgl->resolvent);
#endif
	lglclnstk(&lgl->clause);
	lglpopnunmarkstk(lgl, &lgl->seen);
	lglclnframes(lgl);
}

static void
lglbumpdlit(LGL* lgl, int lit)
{
	int idx = abs(lit), mightneedtorescore = 0;
	QVar* qv = lglqvar(lgl, idx);
	Flt oldscore, newscore;
	if (lgl->simp && !lgl->opts->bumpsimp.val)
		return;
	if (qv->enqueued) {
		assert(lglpeek(&lgl->queue.stk, qv->pos) == abs(lit));
		lglpoke(&lgl->queue.stk, qv->pos, 0);
		lgl->queue.mt++;
		qv->enqueued = 0;
		qv->pos = -1;
		if (!lglval(lgl, lit))
			lgldsched(lgl, idx);
	}
	oldscore = qv->score;
	newscore = lgladdflt(oldscore, lgl->scinc);
	LOG(3,
		"bumping EVSIDS score of variable %d from %s to %s",
		idx,
		lglscr2str(lgl, oldscore),
		lglscr2str(lgl, newscore));
	assert(newscore > oldscore);
	mightneedtorescore = 1;
	qv->score = newscore;
	if (lgldscheduled(lgl, idx))
		lgldup(lgl, idx);
	if (mightneedtorescore && newscore >= lgl->maxscore)
		lglrescorevars(lgl);
}

static void
lglbumpreasonlit(LGL* lgl, int lit)
{
	AVar* av = lglavar(lgl, lit);
	if (av->mark)
		return;
	if (!lglevel(lgl, lit))
		return;
	av->mark = 1;
	lglpushstk(lgl, &lgl->seen, lit);
	LOG(2, "also bumping reason literal %d", lit);
}

static void
lglbumpreason(LGL* lgl, int lit)
{
	int *rsn, tag, other, found;
	const int* p;
	rsn = lglrsn(lgl, lit);
	tag = rsn[0] & MASKCS;
	if (tag == DECISION)
		return;
	if (tag == BINCS || tag == TRNCS) {
		lglbumpreasonlit(lgl, rsn[0] >> RMSHFT);
		if (tag == TRNCS)
			lglbumpreasonlit(lgl, rsn[1]);
	} else {
		assert(tag == LRGCS);
		p = lglidx2lits(lgl, (rsn[0] & REDCS), rsn[1]);
		found = 0;
		while ((other = *p++)) {
			if (other == -lit)
				found++;
			else
				lglbumpreasonlit(lgl, other);
		}
		assert(found == 1);
	}
}

static void
lglbumpreasonlits(LGL* lgl)
{
	int n = lglcntstk(&lgl->seen);
	const int* p = lgl->clause.start;
	while (*p)
		lglbumpreason(lgl, *p++);
	for (p = lgl->seen.start + n; p < lgl->seen.top; p++) {
		AVar* av = lglavar(lgl, *p);
		assert(av->mark);
		av->mark = 0;
	}
	LOG(2, "also bumping %d literals", (int)(lglcntstk(&lgl->seen) - n));
}

static int
lglcmpbump(LGL* lgl, int a, int b)
{
#if 0
  return lgltrail (lgl, b) - lgltrail (lgl, a);
#else
	return lgldcmp(lgl, a, b);
#endif
}

#define LGLCMPBUMP(P, Q) lglcmpbump(lgl, *(P), *(Q))

static void
lglbumpseenlits(LGL* lgl)
{
	const int* p;
	if (lgl->simp && !lgl->opts->bumpsimp.val)
		return;
	lglstart(lgl, &lgl->times->bump);
	lgl->stats->bump.count++;
	if (lgl->opts->bumpreasonlits.val)
		lglbumpreasonlits(lgl);
	SORT(int, lgl->seen.start, lglcntstk(&lgl->seen), LGLCMPBUMP);
	for (p = lgl->seen.start; p < lgl->seen.top; p++)
		lglbumpdlit(lgl, *p);
	LOG(2, "bumped %d literals", lglcntstk(&lgl->seen));
	lgl->stats->bump.lits += lglcntstk(&lgl->seen);
	lglstop(lgl);
}

static void
lglmincls(LGL* lgl, int uip, int glue)
{
	int origsize = lglcntstk(&lgl->clause) - 1;
	int *p, *q, other, minimized, local;
	if (!lgl->opts->minimize.val)
		return;
	if (glue > lgl->opts->minlocalgluelim.val)
		return;
	if (origsize > lgl->opts->minlocalsizelim.val)
		return;
	lglstart(lgl, &lgl->times->mincls);
	lgl->stats->mincls.min++;
	q = lgl->clause.start;
	minimized = 0;
	local =
		lgl->opts->minimize.val <= 1 || glue > lgl->opts->minrecgluelim.val || origsize > lgl->opts->minrecsizelim.val;
	assert(lglmtstk(&lgl->poisoned));
	for (p = q; (other = *p); p++)
		if (other != uip && lglminclslit(lgl, other, local)) {
			LOG(2, "minimized literal %d", other);
			minimized++;
		} else
			*q++ = other;
	*q++ = 0;
	lglclnpoisoned(lgl);
	assert(lgl->clause.top - q == minimized);
	COVER(glue + 1 >= origsize && minimized > 0); // unreachable ...
	LOG(2, "clause minimized by %d literals", minimized);
	LOGCLS(2, lgl->clause.start, "minimized clause");
	lgl->clause.top = q;
	lglstop(lgl);
}

static void
lglsignedmarknpushseen(LGL*, int);
static int
lglsignedmarked(LGL*, int);

static int
lglcmpuiplitrail(LGL* lgl, int uip, int a, int b)
{
	if (a == uip)
		return -1;
	if (b == uip)
		return 1;
	return lgltrail(lgl, a) - lgltrail(lgl, b);
}

#define LGLCMPUIPLITRAIL(P, Q) lglcmpuiplitrail(lgl, uip, *(P), *(Q))

static void
lglredclsassign(LGL* lgl, int lit)
{
	assert(!lglmarked(lgl, lit)), assert(!lglifixed(lgl, lit));
	lglmark(lgl, lit);
	lglpushstk(lgl, &lgl->seen, lit);
	LOG(3, "assigning literal %d for clause reduction", lit);
}

static int
lglredclsmarked(LGL* lgl, int lit)
{
	int mark, val;
	mark = lglmarked(lgl, lit);
	if (mark)
		return mark;
	val = lglifixed(lgl, lit);
	if (val)
		return val;
	return 0;
}

static void
lglredcls(LGL* lgl, int uip, int glue, int* jlevelptr)
{
	int lit, cand, blit, tag, other, other2, count, size, reduced;
	int *q, newjlevel, oldjlevel, level, sat, red, lidx, unit;
	int dec, depth, props, mark, mark2, markcand, lrg;
	const int *p, *w, *eow, *r, *l, *c;
	HTS* hts;
	const int maxdepth = lgl->opts->redclsmaxdepth.val;
	const int maxprops = lgl->opts->redclsmaxprops.val;
	const int maxlrg = lgl->opts->redclsmaxlrg.val;
	const int maxdec = lgl->opts->redclsmaxdec.val;
	const int type = lgl->opts->redclstype.val;
	if (!lgl->opts->redcls.val)
		return;
	if (glue > lgl->opts->redclsglue.val)
		return;
	if (lglcntstk(&lgl->clause) >= (unsigned)lgl->opts->redclsize.val)
		return;
	lglstart(lgl, &lgl->times->redcls);
	lgl->stats->redcls.cls.tried++;
	lglpopnunmarkstk(lgl, &lgl->seen);
	size = lglcntstk(&lgl->clause) - 1;
	assert(size >= 1);
	SORT(int, lgl->clause.start, size, LGLCMPUIPLITRAIL);
	assert(lglpeek(&lgl->clause, 0) == uip);
#ifndef NLGLOG
	if (lgl->opts->log.val >= 2) {
		lglogstart(lgl, 2, "sorted clause before reducing");
		for (p = lgl->clause.start; p < lgl->clause.top; p++) {
			if (!(lit = *p))
				continue;
			fprintf(lgl->out, " %d (l%d,t%d)", lit, lglevel(lgl, lit), lgltrail(lgl, lit));
		}
		lglogend(lgl);
	}
#endif
	sat = reduced = 0;
	assert(lgl->clause.start < lgl->clause.top);
	assert(!lgl->clause.top[-1]);
	dec = 0;
	for (p = q = lgl->clause.start; p < lgl->clause.top; p++) {
		if (!(cand = *p))
			*q++ = cand;
		else {
			lgl->stats->redcls.lits.tried++;
			markcand = lglredclsmarked(lgl, cand);
			if (markcand > 0) {
				LOG(3, "skipping true literal %d for clause reduction", cand);
				lgl->stats->redcls.lits.sat++;
				*q++ = cand;
				sat = 1;
			} else if (markcand < 0) {
				LOG(3, "reducing clause by removing literal %d", cand);
				lgl->stats->redcls.lits.red++;
				reduced++;
			} else {
				assert(!markcand);
				*q++ = cand;
				assert(p + 1 < lgl->clause.top);
				if (!p[1])
					continue;
				if (++dec > maxdec)
					continue;
				LOG(3, "decision literal %d for clause reduction", -cand);
				depth = props = lrg = 0;
				count = lglcntstk(&lgl->seen);
				lglredclsassign(lgl, -cand);
				while (count < (int)lglcntstk(&lgl->seen)) {
					if (props >= maxprops)
						break;
					if (depth >= maxdepth)
						break;
					lit = lglpeek(&lgl->seen, count++);
					if (lit)
						props++;
					else {
						depth++;
						continue;
					}
					assert(lglredclsmarked(lgl, lit) > 0);
					LOG(4, "propagating literal %d for clause reduction", lit);
					hts = lglhts(lgl, -lit);
					w = lglhts2wchs(lgl, hts);
					eow = w + hts->count;
					for (r = w; r < eow; r++) {
						blit = *r;
						tag = blit & MASKCS;
						if (tag == TRNCS || tag == LRGCS)
							r++;
						if (tag == BINCS) {
							other = blit >> RMSHFT;
							if (lglredclsmarked(lgl, other))
								continue;
							lglredclsassign(lgl, other);
						} else if (tag == TRNCS) {
							if (cand != uip || type < 3)
								continue;
							other = blit >> RMSHFT;
							mark = lglredclsmarked(lgl, (other));
							if (mark > 0)
								continue;
							other2 = *r;
							mark2 = lglredclsmarked(lgl, other2);
							if (mark2 > 0)
								continue;
							if (mark2 < 0 && !mark)
								lglredclsassign(lgl, other);
							if (mark < 0 && !mark2)
								lglredclsassign(lgl, other2);
						} else {
							assert(tag == LRGCS);
							if (type < 4 || cand != uip)
								continue;
							if (++lrg >= maxlrg)
								continue;
							other = blit >> RMSHFT;
							mark = lglredclsmarked(lgl, other);
							if (mark > 0)
								continue;
							red = blit & REDCS;
							lidx = *r;
							c = lglidx2lits(lgl, red, lidx);
							unit = 0;
							for (l = c; (other = *l); l++) {
								if (other == -lit)
									continue;
								mark = lglredclsmarked(lgl, other);
								if (mark > 0)
									break;
								if (mark < 0)
									continue;
								if (unit)
									break;
								unit = other;
							}
							if (other || !unit)
								continue;
							lglredclsassign(lgl, unit);
						}
					}
					lglpushstk(lgl, &lgl->seen, 0);
				}
			}
		}
	}
	if (sat)
		lgl->stats->redcls.cls.sat++;
	lgl->clause.top = q;
	LOG(2, "reduced clause by %d literals", reduced);
	LOGCLS(2, lgl->clause.start, "reduced clause");
	if (reduced)
		lgl->stats->redcls.cls.red++;
	oldjlevel = *jlevelptr;
	newjlevel = 0;
	for (p = lgl->clause.start; p < lgl->clause.top; p++) {
		if (!(lit = *p) || lit == uip)
			continue;
		level = lglevel(lgl, lit);
		assert(level != lgl->level);
		if (level > newjlevel)
			newjlevel = level;
	}
	assert(newjlevel <= oldjlevel);
	if (oldjlevel == newjlevel)
		LOG(2, "jump level %d does not change", newjlevel);
	else {
		LOG(2, "jump level reduced to %d from %d", newjlevel, oldjlevel);
		*jlevelptr = newjlevel;
		lgl->stats->redcls.jlevel.red++;
		lgl->stats->redcls.jlevel.sum += oldjlevel - newjlevel;
	}
	lglstop(lgl);
}

static void
lgliassume(LGL* lgl, int lit)
{
	lgl->level++;
	lglpushcontrol(lgl, lit);
	assert(lglcntctk(&lgl->control) == lgl->level + 1);
	LOG(2, "internally assuming %d", lit);
	lglassign(lgl, lit, DECISION, 0);
}

static void
lgldassume(LGL* lgl, int lit)
{
	LOG(1, "next assumed decision %d external %d", lit, lglexport(lgl, lit));
	lgl->stats->decisions++;
	lgliassume(lgl, lit);
	assert(lgldecision(lgl, lit));
}

static int
lglsubl(LGL* lgl, int lidx, int size, int glue)
{
	const int *c, *p;
	int count, lit;
	assert(lidx);
	assert((lidx & GLUEMASK) < MAXGLUE);
	c = lglidx2lits(lgl, REDCS, lidx);
	count = 0;
	for (p = c; count < size && (lit = *p); p++) {
		if (lit == REMOVED)
			return 1;
		if (lglmarked(lgl, lit) > 0)
			count++;
	}
	lgl->stats->subl.tried++;
	if (count < size) {
		LOGCLS(4, c, "only %d literals shared in clause", count);
		return 0;
	}
	assert(!lglrgforcing(lgl, REDCS, lidx));
	LOGCLS(2, c, "subsumed learned (scaled) glue %d clause", (int)(lidx & GLUEMASK));
	LOGCLS(2,
		   lgl->clause.start,
		   "subsumed by new size %d glue %d scaled glue %d clause",
		   size,
		   glue,
		   lglscaleglue(lgl, glue));
	lgl->stats->subl.sub++;
	lgldrupligdelclsaux(lgl, c);
	lglrmlcls(lgl, lidx, REDCS);
	(void)glue;
	return 1;
}

static void
lgldrive(LGL* lgl, const char* type, int* forcedptr, int* glueptr, int* realglueptr, int* jlevelptr)
{
	int lit, jlevel, level, minlevel, maxlevel, glue, realglue, forced;
	const int *p, *soc = lgl->clause.start, *eoc = lgl->clause.top - 1;
	Ctr* c;
	assert(soc <= eoc), assert(!*eoc);
	forced = forcedptr ? *forcedptr : 0;
#ifndef NDEBUG
	for (p = soc; p < eoc; p++) {
		level = lglevel(lgl, lit = *p);
		assert(0 < level), assert(level <= lgl->level);
		c = lgl->control.start + level;
		assert(!c->used2);
	}
#endif
#ifndef NLGLOG
	if (lgl->opts->log.val >= 4) {
		lglogstart(lgl, 2, "driving using clause");
		for (p = soc; p < eoc; p++) {
			lit = *p;
			fprintf(lgl->out, " %d (l%d,t%d)", lit, lglevel(lgl, lit), lgltrail(lgl, lit));
		}
		lglogend(lgl);
	}
#endif
	maxlevel = -1, minlevel = lgl->level + 1;
	for (p = soc; p < eoc; p++) {
		level = lglevel(lgl, *p);
		c = lgl->control.start + level;
		if (!c->used2) {
			if (level < minlevel)
				minlevel = level;
			if (level > maxlevel)
				maxlevel = level;
			c->used2 = 1;
			LOG(4, "level %d used in clause", level);
		} else if (c->used2 == 1) {
			c->used2 = 2;
			LOG(4, "level %d actually used (at least) twice in clause", level);
		}
	}
	if (maxlevel >= 0) {
		c = lgl->control.start + maxlevel;
		assert(c->used2);
		if (c->used2 > 1) {
			assert(c->used2 == 2);
			LOG(4, "maximum level %d used twice so no forced literal", maxlevel);
			jlevel = maxlevel;
			realglue = glue = -1;
			for (p = soc; p < eoc; p++) {
				level = lglevel(lgl, lit = *p);
				c = lgl->control.start + level;
				if (c->used2 || level == jlevel) {
					if (level >= lgl->alevel)
						realglue++;
					glue++;
				}
				c->used2 = 0;
			}
			assert(realglue >= 0);
			assert(glue >= 0);
		} else {
			assert(c->used2 == 1);
			LOG(4, "maximum level %d used only once", maxlevel);
			realglue = glue = jlevel = level = forced = 0;
			for (p = soc; p < eoc; p++) {
				level = lglevel(lgl, lit = *p);
				c = lgl->control.start + level;
				if (level == maxlevel) {
					assert(c->used2 == 1);
					forced = lit;
				} else if (c->used2) {
					if (level > jlevel)
						jlevel = level;
					if (level >= lgl->alevel)
						realglue++;
					glue++;
				}
				c->used2 = 0;
			}
			assert(!jlevel || minlevel <= jlevel);
			assert(jlevel < maxlevel);
			assert(forced);
		}
	} else
		glue = realglue = jlevel = forced = minlevel = maxlevel = 0;

	for (p = soc; p < eoc; p++) {
		level = lglevel(lgl, lit = *p);
		assert(0 < level), assert(level <= lgl->level);
		c = lgl->control.start + level;
		if (c->used2)
			c->used2 = 0;
	}

	LOG(2, "%s minimum level %d", type, minlevel);
	LOG(2, "%s maximum level %d", type, maxlevel);
	LOG(2,
		"%s glue %d (real %d) covers %.0f%%",
		type,
		glue,
		realglue,
		(float)(jlevel ? lglpcnt(glue, (jlevel - minlevel) + 1) : 100.0));
	LOG(2, "%s jump level %d", type, jlevel);
	LOG(2, "%s forced %d", type, forced);

	if (glueptr)
		*glueptr = glue;
	if (realglueptr)
		*realglueptr = realglue;
	if (jlevelptr)
		*jlevelptr = jlevel;
	if (forcedptr)
		*forcedptr = forced;

	(void)type;
}

static void
lgldeco(LGL* lgl, int jlevel, int uip)
{
	int *q, *r, lit, level, limlevel;
#ifndef NDEBUG
	int countabovejlevel = 0;
#endif
	Ctr* p;
	assert(lgl->opts->deco.val == 1);
	if (lgl->opts->deco1opt.val) {
		for (limlevel = jlevel; limlevel > 1; limlevel--) {
			p = lgl->control.start + limlevel;
			if (p->used > 1)
				break;
		}
	} else
		limlevel = jlevel;
	LOG(4, "singly-used level limit %d for deco clause", limlevel);
	for (q = r = lgl->clause.start; q < lgl->clause.top; q++) {
		if (!(lit = *q))
			continue;
		level = lglevel(lgl, lit);
		assert(0 < level);
		if (level > jlevel) {
#ifndef NDEBUG
			assert(!countabovejlevel); // only for UIP
			assert(lit == uip);
			countabovejlevel++;
#else
			(void)uip;
#endif
			continue;
		}
		p = lgl->control.start + level;
		assert(p->used >= 1);
		if (level > limlevel && p->used == 1) {
			LOG(4, "keep singly-used level %d literal %d in deco clause", level, lit);
			*r++ = lit;
		} else if (p->decision != -lit)
			LOG(4, "drop non-decision multi-used level %d literal %d in deco clause", level, lit);
	}
	lgl->clause.top = r;
	for (level = 1; level <= jlevel; level++) {
		p = lgl->control.start + level;
		assert(p < lgl->control.top);
		if (level > limlevel && p->used == 1)
			continue;
		lit = -p->decision;
		assert(lgldecision(lgl, lit));
		assert(lglevel(lgl, lit));
		assert(lglevel(lgl, lit) <= jlevel);
		lglpushstk(lgl, &lgl->clause, lit);
		LOG(4, "add level %d decision literal %d to deco clause", level, lit);
	}
}

/*------------------------------------------------------------------------*/

static const int64_t INT64MIN = LLONG_MIN;
static const int64_t INT64MAX = LLONG_MAX;

static int64_t
lgleftshiftint64(int64_t val, int shift)
{
	int64_t res;
	assert(shift >= 0);
	if (val == INT64MIN)
		res = INT64MIN;
	else if (val == INT64MAX)
		res = INT64MAX;
	else if (val > (INT64MAX >> shift))
		res = INT64MAX;
	else if (val < (INT64MIN >> shift))
		res = INT64MIN;
	else
		res = (int64_t)(((uint64_t)val) << shift);
	assert(val < 0 || res >= val);
	assert(val > 0 || res <= val);
	return res;
}

static int64_t
lglrightshiftint64(int64_t val, int shift)
{
	int64_t res;
	assert(shift >= 0);
	if (val == INT64MIN)
		res = INT64MIN;
	else if (val == INT64MAX)
		res = INT64MAX;
	else
		res = val >> shift;
	assert(val > 0 || res >= val);
	assert(val < 0 || res <= val);
	return res;
}

static int64_t
lgladdint64(int64_t a, int64_t b)
{
	int64_t res;
	if (a == INT64MIN)
		res = INT64MIN;
	else if (b == INT64MIN)
		res = INT64MIN;
	else if (a == INT64MAX)
		res = INT64MAX;
	else if (b == INT64MAX)
		res = INT64MAX;
	else if (a >= 0 && b <= 0)
		res = a + b;
	else if (a <= 0 && b >= 0)
		res = a + b;
	else if (a < 0) {
		assert(b < 0);
		if (b < (INT64MIN - a))
			res = INT64MIN;
		else
			res = a + b;
		assert(res <= a), assert(res <= b);
	} else {
		assert(a > 0), assert(b > 0);
		if (b > (INT64MAX - a))
			res = INT64MAX;
		else
			res = a + b;
		assert(res >= a), assert(res >= b);
	}
	return res;
}

static int64_t
lglsubint64(int64_t a, int64_t b)
{
	int64_t res;
	if (b == INT64MIN)
		res = INT64MIN;
	else
		res = lgladdint64(a, -b);
	return res;
}

static int
lglvalidint64(int64_t a)
{
	return a != INT64MIN && a != INT64MAX;
}

/*------------------------------------------------------------------------*/

static void
lglinitema(LGL* lgl, EMA* ema, int shift)
{
	assert(0 <= shift), assert(shift <= 32);
	ema->shift = shift;
	ema->count = 0;
	LOG(2, "initialized EMA shift %d", shift);
	(void)lgl;
}

static void
lglupdatema(LGL* lgl, EMA* ema, int64_t input, int left)
{
	int64_t oldval, tmpval, newval, add, sub;
	const int shift = ema->count;
	oldval = ema->val;
	if (shift < ema->shift)
		ema->count++;
	assert(lglvalidint64(oldval));
	if (!lglvalidint64(input))
		goto DONE;
	sub = lglrightshiftint64(oldval, shift);
	if (!lglvalidint64(sub))
		goto DONE;
	tmpval = lglsubint64(oldval, sub);
	if (!lglvalidint64(tmpval))
		goto DONE;
	if (left)
		add = lgleftshiftint64(input, 32 - shift);
	else
		add = lglrightshiftint64(input, shift);
	if (!lglvalidint64(add))
		goto DONE;
	newval = lgladdint64(tmpval, add);
	if (!lglvalidint64(newval))
		goto DONE;
	ema->val = newval;
DONE:
#ifndef NLGLOG
	if (left)
		LOG(2,
			"EMA (left) input %lld old %g shift %d new %g",
			(LGLL)input,
			oldval / (double)(1ll << 32),
			ema->shift,
			ema->val / (double)(1ll << 32));
	else
		LOG(2,
			"EMA (right) input %g old %g shift %d new %g",
			input / (double)(1ll << 32),
			oldval / (double)(1ll << 32),
			ema->shift,
			ema->val / (double)(1ll << 32));
#else
	;
#endif
	(void)lgl;
}

#ifndef NLGLDEMA

static void
lglinitdema(LGL* lgl, DEMA* dema, int shift)
{
	lglinitema(lgl, &dema->ema[0], shift);
	lglinitema(lgl, &dema->ema[1], shift);
	LOG(2, "initialized DEMA shift %d", shift);
}

static void
lglupdatedema(LGL* lgl, DEMA* dema, int input)
{
#ifndef NLGLOG
	int64_t oldval = dema->val;
#endif
	int64_t newval, val0, val1;
	lglupdatema(lgl, &dema->ema[0], input, 1);
	if (!lglvalidint64(val0 = dema->ema[0].val))
		goto DONE;
	lglupdatema(lgl, &dema->ema[1], val0, 0);
	if (!lglvalidint64(val1 = dema->ema[1].val))
		goto DONE;
	newval = lglsubint64(lgleftshiftint64(val0, 1), val1);
	if (!newval)
		goto DONE;
	dema->val = newval;
DONE:
	LOG(2,
		"DEMA input %lld old %g shift %d new %g",
		(LGLL)input,
		oldval / (double)(1ll << 32),
		dema->ema[0].shift,
		dema->val / (double)(1ll << 32));
}

#endif

static void
lglinitmacd(LGL* lgl, MACD* macd, int fast, int slow, int smooth)
{
	assert(0 <= fast), assert(fast <= 32);
	assert(0 <= slow), assert(slow <= 32);
	assert(0 <= smooth), assert(smooth <= 32);
	lglinitmacdema(lgl, &macd->fast, fast);
	lglinitmacdema(lgl, &macd->slow, slow);
	lglinitema(lgl, &macd->diff.smoothed, smooth);
	LOG(2, "initialized MACD fast %d slow %d smooth %d", fast, slow, smooth);
}

static void
lglupdatemacd(LGL* lgl, MACD* macd, int64_t input)
{
#if !defined(NDEBUG) || !defined(NLGLOG)
	int64_t oldactual = macd->diff.actual;
	int64_t oldsmoothed = macd->diff.smoothed.val;
#endif
	int64_t newactual;
	assert(lglvalidint64(oldsmoothed));
	assert(lglvalidint64(oldactual));
	lglupdatemacdema(lgl, &macd->fast, input);
	lglupdatemacdema(lgl, &macd->slow, input);
	newactual = lglsubint64(macd->fast.val, macd->slow.val);
	if (!lglvalidint64(newactual))
		goto DONE;
	macd->diff.actual = newactual;
	lglupdatema(lgl, &macd->diff.smoothed, newactual, 0);
DONE:
	LOG(2,
		"updated MACD input %lld actual/smoothed: old %g/%g, new %g/%g",
		(LGLL)input,
		oldactual / (double)(1ll << 32),
		oldsmoothed / (double)(1ll << 32),
		macd->diff.actual / (double)(1ll << 32),
		macd->diff.smoothed.val / (double)(1ll << 32));
}

static void
lglupdstab(LGL* lgl)
{
	int assigned = lglcntstk(&lgl->trail);
	int changed = lgl->stats->stability.changed;
	int topassigned = lglevel2trail(lgl, 1);
	int nontopassigned = assigned - topassigned;
	int64_t difference, shifted, fraction;
	lgl->stats->stability.count++;
	lglprt(lgl,
		   2,
		   "[stability-%lld] %d assigned, %d non-top %.1f%%, %d changed %.2f%%",
		   (LGLL)lgl->stats->stability.count,
		   assigned,
		   nontopassigned,
		   lglpcnt(nontopassigned, assigned),
		   changed,
		   lglpcnt(changed, nontopassigned));
	assert(lgl->level > 0);
	assert(lgl->stats->stability.level > 0);
	assert(lgl->stats->stability.level <= lgl->level);
	assert(nontopassigned >= 0);
	if (!nontopassigned)
		fraction = 0;
	else {
		assert(nontopassigned >= changed);
		difference = nontopassigned - changed;
		shifted = difference << 32;
		fraction = shifted / nontopassigned;
	}
	lglupdatema(lgl, &lgl->stats->stability.avg, fraction, 0);
	lgl->stats->stability.level = 0;
	lglprt(lgl,
		   2,
		   "[stability-%lld] unchanged fraction %.3f%% new stability %.3f%%",
		   (LGLL)lgl->stats->stability.count,
		   100.0 * fraction / (double)(1ll << 32),
		   100.0 * lgl->stats->stability.avg.val / (double)(1ll << 32));
}

static void
lglinitavg(AVG* avg)
{
	avg->val = avg->count = 0;
}

static void
lglupdateavg(LGL* lgl, AVG* avg, int input)
{
	int64_t oldval = avg->val, newval = oldval, shifted, diff;
	shifted = lgleftshiftint64(input, 32);
	if (!lglvalidint64(shifted))
		goto DONE;
	diff = lglsubint64(shifted, oldval);
	if (!lglvalidint64(diff))
		goto DONE;
	diff /= ++avg->count;
	newval = lgladdint64(oldval, diff);
	if (!lglvalidint64(newval))
		goto DONE;
	avg->val = newval;
DONE:
	LOG(2,
		"AVG input %d old %g new %g count %lld",
		input,
		oldval / (double)(1ll << 32),
		newval / (double)(1ll << 32),
		avg->count);
	(void)lgl;
}

/*------------------------------------------------------------------------*/

static int
lglblockrestart(LGL* lgl)
{
	const int64_t actual = ((int64_t)lglcntstk(&lgl->trail)) << 32;
	const int64_t smoothed = lgl->stats->tlevel.val;
	const int64_t percent = lgl->opts->restartblocklim.val;
	int64_t limit;
	int blocking;
	if (lgl->stats->confs < lgl->limits->restart.confs)
		return 0;
	if (lgl->stats->confs < lgl->opts->restartblockbound.val)
		return 0;
	assert(percent);
	assert(lglvalidint64(smoothed));
	limit = (smoothed < 0 ? -smoothed : smoothed);
	assert(limit >= 0);
	if (!lglvalidint64(limit))
		return 0;
	limit /= 100;
	if (INT64MAX / percent < limit)
		return 0;
	limit *= percent;
	blocking = (limit < actual);
	LOG(2,
		"%s restart MACD %g * %.2f = %g %s %g at %lld conflicts",
		(blocking ? "blocking" : "not blocking"),
		smoothed / (double)(1ll << 32),
		lgl->opts->restartblocklim.val / 100.0,
		limit / (double)(1ll << 32),
		blocking ? "<" : ">=",
		actual / (double)(1ll << 32),
		(LGLL)lgl->stats->confs);
	return blocking;
}

static void
lglrndcls(LGL* lgl, int len, int prune)
{
	int64_t delta;
	if (len == 2) {
		lglrep(lgl, 1 + !prune, prune ? 'B' : 'b');
		lgl->stats->bins++;
	}
	if (len == 3) {
		lglrep(lgl, 1 + !prune, prune ? 'T' : 't');
		lgl->stats->trns++;
	}
	delta = lgl->stats->confs - lgl->confatlastit;
	LOG(1, "iteration delta %lld", (LGLL)delta);
	lglupdatemacd(lgl, &lgl->stats->its.avg, delta);
	if (len == 1) {
		int delay = lgl->opts->simpitdelay.val;
		lgl->confatlastit = lgl->stats->confs;
		lgl->stats->its.count++;
		lglrep(lgl, 1, prune ? 'I' : 'i');
		if (delta > 0)
			delay /= delta;
		if (delay > 0) {
			lgl->limits->simp.confs += delay;
			LOG(1, "delaying simplification by %d conflicts", delay);
		}
	}
	LOG(1, "iteration interval average %.0f", lglitavg(lgl));
}

static int
lglana(LGL* lgl)
{
	int open, resolved, tag, lit, uip, r0, r1, other, other2, *p, *q, lidx;
	int size, savedsize, resolventsize, level, red, glue, realglue;
	int jlevel, mlevel, nmlevel, origsize, redsize, tlevel;
	int len, *rsn;
#ifdef RESOLVENT
	AVar* av;
#endif
	if (lgl->mt)
		return 0;
	if (lgl->failed)
		return 0;
	if (!lgl->conf.lit)
		return 1;
	if (!lgl->level) {
		lglmt(lgl);
		return 0;
	}
	lglstart(lgl, &lgl->times->analysis);
	if (lgl->sweeping)
		lgl->stats->sweep.confs++;
	else
		assert(!lgl->simp), lgl->stats->confs++;
RESTART:
	tlevel = lglcntstk(&lgl->trail);
	assert(lgl->conf.lit);
	assert(lglmtstk(&lgl->seen));
	assert(lglmtstk(&lgl->clause));
	assert(lglmtstk(&lgl->resolvent));
#ifndef NDEBUG
	if (lgl->opts->check.val > 0)
		lglchkclnvar(lgl);
#endif
	open = 0;
	lit = lgl->conf.lit, r0 = lgl->conf.rsn[0], r1 = lgl->conf.rsn[1];
	savedsize = resolved = 0;
	open += lglpull(lgl, lit);
	LOG(2, "starting analysis with reason of literal %d", lit);
	for (;;) {
		LOGREASON(2, lit, r0, r1, "analyzing");
		if (resolved++) {
#ifdef RESOLVENT
			if (lglmaintainresolvent(lgl)) {
				LOG(2, "removing %d from resolvent", -lit);
				lglrmstk(&lgl->resolvent, -lit);
				LOGRESOLVENT(3, "resolvent after removing %d is", -lit);
			}
#endif
		}
		assert(lglevel(lgl, lit) == lgl->level);
		tag = r0 & MASKCS;
		if (tag == BINCS || tag == TRNCS) {
			other = r0 >> RMSHFT;
			size = lglevel(lgl, other) ? 2 : 1;
			if (lglpull(lgl, other))
				open++;
			if (tag == TRNCS) {
				if (lglevel(lgl, r1))
					size++;
				if (lglpull(lgl, r1))
					open++;
			}
		} else {
			assert(tag == LRGCS);
			red = r0 & REDCS;
			p = lglidx2lits(lgl, red, r1);
			size = 0;
			while ((other = *p++)) {
				if (lglevel(lgl, other))
					size++;
				if (lglpull(lgl, other))
					open++;
			}
			if (red)
				lglbumpnupdatelidx(lgl, r1);
		}
		LOG(3,
			"open %d antecendents %d learned %d resolved %d",
			open - 1,
			size,
			lglcntstk(&lgl->clause),
			lglcntstk(&lgl->resolvent));
		assert(open > 0);
		resolventsize = open + lglcntstk(&lgl->clause);
#ifdef RESOLVENT
		LOGRESOLVENT(2, "resolvent");
		if (lglmaintainresolvent(lgl))
			assert((int)lglcntstk(&lgl->resolvent) == resolventsize);
#endif
		if (lgl->opts->otfs.val && (resolved >= 2) && resolventsize > 1 &&
			(resolventsize < size || (resolved == 2 && resolventsize < savedsize))) {
			int sl = 0, s0 = 0, s1 = 0, del;
			int cl = lgl->conf.lit;
			int c0 = lgl->conf.rsn[0];
			int c1 = lgl->conf.rsn[1];
			assert(resolved >= 2);
			del = lit;
			if (resolved > 2)
				;
			else if (resolventsize >= size)
				del = -lit, lit = cl, r0 = c0, r1 = c1;
			else if (resolventsize >= savedsize)
				;
			else {
				if (r0 & REDCS) {
					sl = lit, s0 = r0, s1 = r1;
					del = -lit, lit = cl, r0 = c0, r1 = c1;
				} else
					sl = cl, s0 = c0, s1 = c1;
			}
			lgldynstr(lgl, del, lit, r0, r1);
			if (sl)
				lgldynsub(lgl, sl, s0, s1);
			lit = lgl->conf.lit;
			r0 = lgl->conf.rsn[0];
			r1 = lgl->conf.rsn[1];
			assert(lglevel(lgl, lit) == lgl->level);
			tag = r0 & MASKCS;
			if (tag == UNITCS) {
				mlevel = lglevel(lgl, lit);
				nmlevel = 1;
				jlevel = 0;
			} else if (tag == BINCS) {
				jlevel = lglevel(lgl, lit);
				other = r0 >> RMSHFT;
				mlevel = lglevel(lgl, other);
				if (jlevel > mlevel) {
					nmlevel = 1;
					SWAP(int, mlevel, jlevel);
				} else if (jlevel == mlevel)
					nmlevel = 2;
				else
					assert(jlevel < mlevel), nmlevel = 1;
			} else if (tag == TRNCS) {
				jlevel = lglevel(lgl, lit);
				other = r0 >> RMSHFT;
				level = lglevel(lgl, other);
				other2 = r1;
				mlevel = lglevel(lgl, other2);
				if (jlevel > mlevel)
					SWAP(int, jlevel, mlevel);
				if (level > mlevel)
					SWAP(int, level, mlevel);
				if (level > jlevel)
					SWAP(int, level, jlevel);
				assert(mlevel >= jlevel);
				assert(jlevel >= level);
				if (mlevel == jlevel)
					nmlevel = 2 + (jlevel == level);
				else
					assert(mlevel > jlevel), nmlevel = 1;
				assert(nmlevel == 1 + (mlevel == jlevel) + (mlevel == level));
			} else {
				mlevel = lglevel(lgl, lit), nmlevel = 1, jlevel = 0;
				assert(tag == LRGCS);
				red = r0 & REDCS;
				p = lglidx2lits(lgl, red, r1);
				while (jlevel < lgl->level && (other = *p++)) {
					if (other == lit)
						continue;
					assert(other != -lit);
					level = lglevel(lgl, other);
					if (level < mlevel) {
						if (level > jlevel)
							jlevel = level;
						continue;
					}
					if (level == mlevel)
						nmlevel++;
					else {
						assert(level > mlevel);
						jlevel = mlevel;
						mlevel = level;
						nmlevel = 1;
					}
				}
				if (red)
					lglbumpnupdatelidx(lgl, r1);
			}
			if (nmlevel >= 2) {
				LOG(2, "restarting analysis after on-the-fly strengthening");
				lgl->stats->otfs.restarting++;
				lglclnana(lgl);
				if (lgl->level > mlevel)
					lglbacktrack(lgl, mlevel);
				goto RESTART;
			}
			assert(nmlevel == 1);
			assert(mlevel > jlevel);
			assert(lglevel(lgl, lit) == mlevel);
			LOGREASON(2, lit, r0, r1, "driving %d at level %d through strengthened", lit, jlevel);
			lgl->stats->otfs.driving++;
			lglbacktrack(lgl, jlevel);
			if (tag == LRGCS && (r0 & REDCS) && (r1 & GLUEMASK) == MAXGLUE) {
				// Note, that 'lit' is unassigned by 'backtrack' above and thus if
				// it's reason is at the end of the MAXGLUE stack, then the reason
				// becomes invalid.  See the corresponding 'assert' in 'lglassign'.
				// Our original incorrect fix to this problem was to simply increase
				// the size of the stack to point after the zero sentinel, which
				// however produces garbage on the MAXGLUE stack and thus we only
				// follow this idea if the reason of 'lit' is exactly after the
				// current end of the MAXGLUE stack. If it starts before the end
				// nothing has to be done.  Otherwise we have to move the reason
				// literals and update the reason.
				p = lglidx2lits(lgl, REDCS, r1);
				if (p > lgl->red[MAXGLUE].top) {
					rsn = lglrsn(lgl, lit);
					r1 = lglcntstk(&lgl->red[MAXGLUE]);
					r1 <<= GLUESHFT;
					r1 |= MAXGLUE;
					rsn[1] = r1;
					for (q = lgl->red[MAXGLUE].top; *p; p++) {
						assert(q < p);
						*q++ = *p;
					}
					*q++ = 0;
					lgl->red[MAXGLUE].top = q;
				} else if (p == lgl->red[MAXGLUE].top) {
					while (*p) {
						assert(*p < NOTALIT);
						p++;
					}
					if (++p > lgl->red[MAXGLUE].top)
						lgl->red[MAXGLUE].top = p;
				}
			}
			lglassign(lgl, lit, r0, r1);
			len = INT_MAX;
			goto DONE;
		}
		savedsize = size;
		while (!lglmarked(lgl, lit = lglpopstk(&lgl->trail)))
			lglunassign(lgl, lit);
		lglunassign(lgl, lit);
		if (!--open) {
			uip = -lit;
			break;
		}
		LOG(2, "analyzing reason of literal %d next", lit);
		rsn = lglrsn(lgl, lit);
		r0 = rsn[0], r1 = rsn[1];
	}
	assert(uip);
	LOG(2, "adding UIP %d at same level %d to 1st UIP clause", uip, lgl->level);
	lglpushstk(lgl, &lgl->clause, uip);
	assert(lglmarked(lgl, uip));
#ifdef RESOLVENT
	LOGRESOLVENT(3, "final resolvent before flushing fixed literals");
	if (lglmaintainresolvent(lgl)) {
		int* q = lgl->resolvent.start;
		for (p = q; p < lgl->resolvent.top; p++)
			if (lglevel(lgl, (other = *p)))
				*q++ = other;
		lgl->resolvent.top = q;
		LOGRESOLVENT(2, "final resolvent after flushing fixed literals");
		assert(lglcntstk(&lgl->resolvent) == lglcntstk(&lgl->clause));
		for (p = lgl->clause.start; p < lgl->clause.top; p++)
			assert(lglavar(lgl, *p)->mark > 0);
		for (p = lgl->resolvent.start; p < lgl->resolvent.top; p++) {
			av = lglavar(lgl, *p);
			assert(av->mark > 0);
			av->mark = -av->mark;
		}
		for (p = lgl->clause.start; p < lgl->clause.top; p++)
			assert(lglavar(lgl, *p)->mark < 0);
		for (p = lgl->resolvent.start; p < lgl->resolvent.top; p++) {
			av = lglavar(lgl, *p);
			assert(av->mark < 0);
			av->mark = -av->mark;
		}
		lglclnstk(&lgl->resolvent);
	}
#endif
	lglpushstk(lgl, &lgl->clause, 0);
	LOGCLS(2, lgl->clause.start, "1st UIP clause");

	lgldrive(lgl, "preliminary", 0, &glue, 0, &jlevel);
	assert(glue == (int)lglcntstk(&lgl->frames));

	origsize = lglcntstk(&lgl->clause) - 1;
	assert(glue + 1 <= origsize);
	lglbumpseenlits(lgl);
	lgl->stats->lits.nonmin += origsize;
	redsize = jlevel + 1;
	if (origsize == 2) {
		lgl->stats->mincls.bin++;
		LOGCLS(2, lgl->clause.start, "binary 1st UIP clause not minimized");
	} else if (glue + 1 == origsize) {
		lgl->stats->mincls.size++;
		LOGCLS(2, lgl->clause.start, "size %d 1st UIP clause with glue %d can not be minimized", origsize, glue);
	} else if (jlevel > 1 && lgl->opts->deco.val && redsize < 2 * origsize / 3 && glue > lgl->opts->decolim.val) {
		assert(origsize > 2);
		lgl->stats->mincls.deco++;
		lgl->stats->deco.sum.orig += origsize;
		lgldeco(lgl, jlevel, uip);
		lglpushstk(lgl, &lgl->clause, uip);
		lgl->stats->deco.sum.red += lglcntstk(&lgl->clause);
		lglpushstk(lgl, &lgl->clause, 0);
		LOGCLS(2,
			   lgl->clause.start,
			   "size %d 1st UIP clause discarded for size %d decision-only clause",
			   origsize,
			   redsize);
	} else {
		lglmincls(lgl, uip, glue);
		lglredcls(lgl, uip, glue, &jlevel);
	}

	lgldrive(lgl, "final", &uip, &glue, &realglue, &jlevel);

	if (uip && lglrsn(lgl, uip)[0])
		lgl->stats->uips++;
	else if (jlevel + 1 == lgl->level)
		lgl->stats->decflipped++;

	lglbumpscinc(lgl);

	lglbacktrack(lgl, jlevel);

	len = lglcntstk(&lgl->clause) - 1;
	lgl->stats->clauses.glue += glue;
	lgl->stats->clauses.realglue += realglue;
	lgl->stats->lits.learned += len;
	lgl->stats->clauses.learned++;

	if (!lgl->simp) {
		LOG(2, "updating AVG glue");
		lglupdateavg(lgl, &lgl->stats->avglue, realglue);
		LOG(2, "updating MACD restart glue");
		lglupdatemacd(lgl, &lgl->stats->glue, realglue);
		LOG(2, "updating MACD jump level");
		lglupdatemacd(lgl, &lgl->stats->jlevel, jlevel);
		LOG(2, "updating EMA trail level");
		lglupdatema(lgl, &lgl->stats->tlevel, tlevel, 1);
		if (lgl->opts->restartblock.val == 1 && lglblockrestart(lgl)) {
			lgl->limits->restart.confs = lgl->stats->confs + lgl->opts->restartint.val;
			lgl->stats->restarts.blocked++;
		}
#ifndef NLGLFILES
		{
			static FILE* macdfile = 0;
			if (!macdfile)
				macdfile = fopen("/tmp/macd", "w");
			fprintf(macdfile,
					"%lld %d"
					" %g %g %g"
					"\n",
					(LGLL)lgl->stats->confs,
					realglue,
					lgl->stats->glue.fast.val / (double)(1ll << 32),
					lgl->stats->avglue.val / (double)(1ll << 32),
					lgl->stats->glue.slow.val / (double)(1ll << 32));

			fflush(macdfile);
		}
#endif
	}

	lgldrupligaddcls(lgl, REDCS);
	lidx = lgladdcls(lgl, REDCS, realglue, 1);

#ifndef NDEBUG
	if (!lglmtstk(&lgl->prevclause)) {
		for (p = lgl->prevclause.start, q = lgl->clause.start; *p; p++, q++)
			if (*p != *q)
				break;
		assert(*q != *p);
	}
	lglclnstk(&lgl->prevclause);
	for (p = lgl->clause.start; *p; p++)
		lglpushstk(lgl, &lgl->prevclause, *p);
	lglpushstk(lgl, &lgl->prevclause, 0);
	lgl->prevglue = lglscaleglue(lgl, realglue);
#endif
	if (lgl->opts->subl.val) {
		lglstart(lgl, &lgl->times->subl);
		lgl->stats->subl.count++;
		lglpopnunmarkstk(lgl, &lgl->seen);
		size = lglcntstk(&lgl->clause) - 1;
		if (size > 1) {
			unsigned sig = 0;
			p = lgl->clause.start;
			while ((lit = *p++)) {
				sig |= (1u << (lglulit(lit) & 31));
				lglmark(lgl, lit);
			}
			q = lgl->learned.start;
			for (p = q; p < lgl->learned.top; p += 3) {
				int olidx = p[0], osize = p[1];
				unsigned osig = (unsigned)p[2];
				if (olidx < 0)
					continue;
				if (osize <= size || (sig & ~osig) || !lglsubl(lgl, olidx, size, glue))
					*q++ = olidx, *q++ = osize, *q++ = (int)osig;
			}
			lgl->learned.top = q;
			if (lidx && (lidx & GLUEMASK) < MAXGLUE) {
				lgl->stats->subl.cands++;
				lglpushstk(lgl, &lgl->learned, lidx);
				lglpushstk(lgl, &lgl->learned, size);
				lglpushstk(lgl, &lgl->learned, (int)sig);
				if ((int)lglcntstk(&lgl->learned) / 3 > lgl->opts->subl.val) {
					q = lgl->learned.start;
					for (p = q + 3; p < lgl->learned.top; p++)
						*q++ = *p;
					lgl->learned.top = q;
				}
			}
			p = lgl->clause.start;
			while ((lit = *p++))
				lglunmark(lgl, lit);
		}
		lglstop(lgl);
	}

	if (lgl->cbs && lgl->cbs->cls.produce.fun && glue <= lgl->opts->synclsglue.val &&
		(int)lglcntstk(&lgl->clause) - 1 <= lgl->opts->synclslen.val) {
		assert(lglmtstk(&lgl->eclause));
		for (p = lgl->clause.start; *p; p++)
			lglpushstk(lgl, &lgl->eclause, lglexport(lgl, *p));
		lglpushstk(lgl, &lgl->eclause, 0);
		LOGCLS(2,
			   lgl->eclause.start,
			   "exporting external redundant glue %d length %d clause",
			   glue,
			   lglcntstk(&lgl->clause) - 1);
		lgl->stats->sync.cls.produced++;
		lgl->cbs->cls.produce.fun(lgl->cbs->cls.produce.state, lgl->eclause.start, glue);
		lglclnstk(&lgl->eclause);
	}

DONE:

	if (!lgl->simp)
		lglrndcls(lgl, len, 0);
	lglclnana(lgl);
	lglstop(lgl);

	return 1;
}

static void
lglincrestart(LGL* lgl)
{
	Stats* s = lgl->stats;
	int64_t delta;
	int pen = 0;
	delta = lgl->opts->restartint.val;
	if (lgl->opts->restartfixed.val)
		goto NOPEN;
	LOG(2, "initial next restarting delta %lld", (LGLL)delta);
	if (lgl->opts->restartpen1.val && s->confs > 500000 && (!s->its.count || s->confs / s->its.count > 100000)) {
		lglprt(lgl, 2, "[restart-%lld] few iterations after 500000 conflicts", (LGLL)lgl->stats->restarts.count);
		pen++;
	}
	if (lgl->opts->restartpen2.val && s->confs > 50000 && (!s->bins || s->confs / s->bins > 10000)) {
		lglprt(lgl, 2, "[restart-%lld] few binaries after 50000 conflicts", (LGLL)lgl->stats->restarts.count);
		pen++;
	}
	if (lgl->opts->restartpen3.val && s->confs > 5000 && (!s->trns || s->confs / s->trns > 1000)) {
		lglprt(lgl, 2, "[restart-%lld] few ternaries after 5000 conflicts", (LGLL)lgl->stats->restarts.count);
		pen++;
	}
	if (lgl->opts->restartpenstab.val && s->confs > 50000) {
		int64_t glue = (s->glue.slow.val >> 32);
		if (glue < 20) {
			int64_t stab = s->stability.avg.val;
			int peninc = 0;
			assert(stab <= (1ll << 32));
			stab *= 100000;
			stab >>= 32;
			if (stab > 98000)
				peninc++;
			if (stab > 99000)
				peninc++;
			if (stab > 99500)
				peninc++;
			if (stab > 99750)
				peninc++;
			if (stab > 99875)
				peninc++;
			pen += peninc;
			lglprt(lgl,
				   2,
				   "[restart-%lld] "
				   "stability %lld glue %d after 50000 conflicts (penalty increase %d)",
				   (LGLL)lgl->stats->restarts.count,
				   stab,
				   glue,
				   peninc);
		}
	}
NOPEN:
	delta <<= pen;
	lgl->stats->restarts.delta.pen += pen;
	lgl->stats->restarts.delta.delta += delta;
	lgl->stats->restarts.delta.count++;
	lgl->limits->restart.confs = lgl->stats->confs + delta;
	lglprt(lgl,
		   2,
		   "[restart-%lld] next restart %lld delta %lld (penalty %d)",
		   (LGLL)lgl->stats->restarts.count,
		   (LGLL)lgl->limits->restart.confs,
		   (LGLL)delta,
		   pen);
	lglrep(lgl, 2, 'R');
}

static void
lglqueueflush(LGL* lgl)
{
	int before, mt, after, nonfree;
	const int *p, *start;
	int *q, lit, found;
	QVar* qv;
	lgl->stats->queue.flushed++;
	found = nonfree = mt = 0;
	before = lglcntstk(&lgl->queue.stk);
	assert(lgl->mt <= before);
	start = q = lgl->queue.stk.start;
	for (p = q; p < lgl->queue.stk.top; p++) {
		if (!(lit = *p)) {
			mt++;
			continue;
		}
		qv = lglqvar(lgl, lit);
		assert(qv->enqueued);
		assert(qv->pos == (p - start));
		if (!lglisfree(lgl, lit)) {
			qv->enqueued = 0;
			qv->pos = -1;
			nonfree++;
		} else {
			qv->pos = (q - start);
			if (!found && lgl->queue.next >= (p - start))
				found = 1, lgl->queue.next = qv->pos;
			*q++ = lit;
		}
	}
	lgl->queue.stk.top = q;
	if (!found)
		lgl->queue.next = lglcntstk(&lgl->queue.stk) - 1;
	assert(mt == lgl->queue.mt);
	lgl->queue.mt = 0;
	after = lglcntstk(&lgl->queue.stk);
	assert(before == after + nonfree + mt);
	lglprt(lgl,
		   2,
		   "[flush-queue-%d] flushed %d = %d mt + %d nonfree (resized %d to %d)",
		   lgl->stats->queue.flushed,
		   mt + nonfree,
		   mt,
		   nonfree,
		   before,
		   after);
}

static int
lglnextdecision(LGL* lgl, int updatestats)
{
	int res = 0;
#ifndef NDEBUG
	int start;
#endif
	QVar* qv;
	if (!lgl->unassigned)
		return 0;
	lglstart(lgl, &lgl->times->decide);
	lglstart(lgl, &lgl->times->heapdecision);
	while (!res && !lglmtstk(&lgl->dsched)) {
		int next = lgltopdsched(lgl);
		if (!lglval(lgl, next) && lglisfree(lgl, next))
			res = next;
		else
			lglpopdsched(lgl);
	}
	lglstop(lgl);
	if (!res) {
		lglstart(lgl, &lgl->times->queuedecision);
		assert(!lglmtstk(&lgl->queue.stk));
		if ((int)lglcntstk(&lgl->queue.stk) < 2 * lgl->queue.mt)
			lglqueueflush(lgl);
#ifndef NDEBUG
		start = lgl->queue.next;
#endif
		for (;;) {
			res = lglpeek(&lgl->queue.stk, lgl->queue.next);
			if (res) {
				qv = lglqvar(lgl, res);
				assert(qv->enqueued);
				assert(qv->pos == lgl->queue.next);
				if (!lglisfree(lgl, res)) {
					lglpoke(&lgl->queue.stk, lgl->queue.next, 0);
					lgl->queue.mt++;
					qv->enqueued = 0;
					qv->pos = -1;
				} else if (!lglval(lgl, res)) {
					if (updatestats)
						lgl->stats->qdecs++;
					break;
				}
			}
			lgl->queue.next--;
			if (lgl->queue.next < 0)
				lgl->queue.next = lglcntstk(&lgl->queue.stk) - 1;
			ASSERT(lgl->queue.next != start);
		}
		lglstop(lgl);
	} else if (updatestats)
		lgl->stats->hdecs++;
	assert(res);
	lglstop(lgl);
	LOG(2, "next decision would be %d", res);
	return res;
}

static int
lglreusetrail(LGL* lgl)
{
	int next = 0, res = 0, prev, level;
	const Ctr* p;
	if (!lgl->opts->reusetrail.val)
		return 0;
	if (!(next = lglnextdecision(lgl, 0)))
		return 0;
	for (p = lgl->control.start + 1; p < lgl->control.top; p++) {
		prev = p->decision;
		assert(lgldecision(lgl, prev));
		if (!lglassumption(lgl, prev) && lgldcmp(lgl, prev, next) < 0)
			break;
		level = lglevel(lgl, prev);
		assert(level == p - lgl->control.start);
		assert(res + 1 == level);
		res = level;
	}
	if (res)
		lglprt(lgl, 3, "[reuse-trail] reusing level %d from current level %d", res, lgl->level);
	else
		lglprt(lgl, 3, "[reuse-trail] can not reuse any level from current level %d", lgl->level);
	return res;
}

static void
lglrestart(LGL* lgl)
{
	int level, kept;
	assert(lgl->opts->restart.val);
	lglstart(lgl, &lgl->times->restart);
	level = lglreusetrail(lgl);
	if (level < lgl->alevel)
		level = lgl->alevel;
	else if (level > lgl->alevel) {
		kept = (100 * level) / lgl->level;
		lgl->stats->restarts.kept.sum += kept;
		lgl->stats->restarts.kept.count++;
	}
	if (level < lgl->level) {
		int oldlevel = lgl->level;
		lglbacktrack(lgl, level);
		LOG(2, "setting stability level to %d", oldlevel);
		lgl->stats->stability.level = oldlevel;
		assert(lgl->stats->stability.level > 0);
		lgl->stats->stability.changed = 0;
		lgl->stats->restarts.count++;
	} else {
		lgl->stats->restarts.reused++;
		lgl->stats->restarts.skipped++;
	}
	lglincrestart(lgl);
	lglstop(lgl);
}

static void
lgldefrag(LGL* lgl)
{
	int *wchs, i, idx, bit, ldsize, size, *start, *q, *end;
	const int *p, *eow, *w;
	unsigned offset;
	long nwchs;
	HTS* hts;
	lglstart(lgl, &lgl->times->defrag);
	lgl->stats->defrags++;
	nwchs = lglcntstk(&lgl->wchs->stk);
	NEW(wchs, nwchs);
	memcpy(wchs, lgl->wchs->stk.start, nwchs * sizeof *wchs);
	for (i = 0; i < MAXLDFW; i++)
		lgl->wchs->start[i] = UINT_MAX;
	lgl->wchs->free = 0;
	start = lgl->wchs->stk.start;
	assert(nwchs >= 1);
	assert(start[0] == (int)UINT_MAX);
	offset = 1;
	for (idx = 2; idx < lgl->nvars; idx++)
		for (bit = 0; bit <= 1; bit++) {
			hts = lgl->dvars[idx].hts + bit;
			if (!hts->offset) {
				assert(!hts->count);
				continue;
			}
			ldsize = lglceild(hts->count);
			size = (1 << ldsize);
			assert((unsigned)size >= hts->count);
			w = wchs + hts->offset;
			hts->offset = offset;
			eow = w + hts->count;
			q = start + offset;
			for (p = w; p < eow; p++)
				*q++ = *p;
			offset += size;
			end = start + offset;
			while (q < end)
				*q++ = 0;
		}
	DEL(wchs, nwchs);
	q = start + offset;
	*q++ = (int)UINT_MAX;
	assert(q <= lgl->wchs->stk.top);
	lgl->wchs->stk.top = q;
	lglfitstk(lgl, &lgl->wchs->stk);
	lgl->limits->dfg = lgl->stats->pshwchs + lgl->opts->defragint.val;
	lglrep(lgl, 2, 'F');
	lglstop(lgl);
}

static void
lgldis(LGL* lgl)
{
	int blit, nblit, tag, red, *p, *q, *eow, *w;
	int idx, sign, lit, other, other2;
	Stk bins, trns;
	Val val, val2;
	HTS* hts;
	assert(!lgl->level);
	CLR(bins);
	CLR(trns);
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2) {
			lit = idx * sign;
			hts = lglhts(lgl, lit);
			if (!hts->offset)
				continue;
			val = lglval(lgl, lit);
			assert(hts->count > 0);
			if (lgl->mt || val || lgliselim(lgl, lit)) {
				lglshrinkhts(lgl, hts, 0);
				continue;
			}
			assert(lglisfree(lgl, lit));
			assert(lglmtstk(&bins));
			assert(lglmtstk(&trns));
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				red = blit & REDCS;
				if (tag == OCCS)
					continue;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (tag == LRGCS)
					continue;
				other = blit >> RMSHFT;
				val = lglval(lgl, other);
				if (val > 0)
					continue;
				if (lgliselim(lgl, other))
					continue;
				if (tag == BINCS) {
					assert(!val);
					lglpushstk(lgl, &bins, blit);
					continue;
				}
				assert(tag == TRNCS);
				other2 = *p;
				val2 = lglval(lgl, other2);
				if (val2 > 0)
					continue;
				if (lgliselim(lgl, other2))
					continue;
				if (val < 0) {
					assert(val < 0 && !val2);
					nblit = red | RMSHFTLIT(other2) | BINCS;
					lglpushstk(lgl, &bins, nblit);
					if (abs(lit) < abs(other2)) {
						lgldrupligaddclsarg(lgl, REDCS, lit, other2, 0);
						lgldrupligdelclsarg(lgl, lit, other, other2, 0);
					}
					continue;
				}
				if (val2 < 0) {
					assert(!val && val2 < 0);
					nblit = red | RMSHFTLIT(other) | BINCS;
					lglpushstk(lgl, &bins, nblit);
					if (abs(lit) < abs(other)) {
						lgldrupligaddclsarg(lgl, REDCS, lit, other, 0);
						lgldrupligdelclsarg(lgl, lit, other, other2, 0);
					}
					continue;
				}
				assert(!val && !val2);
				lglpushstk(lgl, &trns, blit);
				lglpushstk(lgl, &trns, other2);
			}
			q = w;
			for (p = bins.start; p != bins.top; p++)
				*q++ = *p;
			for (p = trns.start; p != trns.top; p++)
				*q++ = *p;
			lglshrinkhts(lgl, hts, q - w);
			lglclnstk(&bins);
			lglclnstk(&trns);
		}
	lglrelstk(lgl, &bins);
	lglrelstk(lgl, &trns);
	lglrelstk(lgl, &lgl->learned);
}

static int
lglcmphase(LGL* lgl, int a, int b)
{
	return lgliphase(lgl, b) - lgliphase(lgl, a);
}

#define LGLCMPHASE(A, B) lglcmphase(lgl, *(A), *(B))

static void
lglconnaux(LGL* lgl, int glue)
{
	int lit, collect, lidx, size, origsize, red, act;
	const int druplig = lgl->opts->druplig.val;
	const int *p, *c, *start, *top;
	Stk *stk, saved;
	int *q, *d;
	Val val;
	if (glue >= 0) {
		assert(glue < MAXGLUE);
		red = REDCS;
		stk = lgl->red + glue;
	} else
		red = 0, stk = &lgl->irr;
	start = q = stk->start;
	top = stk->top;
	CLR(saved);
	for (c = q; c < top; c = p + 1) {
		act = *c;
		if (act == REMOVED) {
			for (p = c + 1; p < top && *p == REMOVED; p++)
				;
			assert(p >= top || *p < NOTALIT || lglisact(*p));
			p--;
			continue;
		}
		if (lglisact(act))
			assert(glue >= 0), *q++ = *c++;
		else
			assert(glue < 0), act = -1;
		d = q;
		collect = 0;
		lglclnstk(&saved);
		for (p = c; assert(p < top), (lit = *p); p++) {
			assert(lit < NOTALIT);
			if (druplig)
				lglpushstk(lgl, &saved, lit);
			if (collect)
				continue;
			val = lglval(lgl, lit);
			if (lgliselim(lgl, lit)) {
				assert(lgl->eliminating || lgl->blocking);
				collect = 1;
			} else if (val > 0)
				collect = 1;
			else if (!val)
				*q++ = lit;
		}

		size = q - d;
		origsize = p - c;
		assert(!druplig || origsize == (int)lglcntstk(&saved));
		assert(size <= origsize);

		if (druplig && !collect && size > 1 && size < origsize)
			*q = 0, lgldrupligaddclsaux(lgl, REDCS, d);

		if (collect || p == c + 1) {
			q = d - (act >= 0);
		} else if (!size) {
			q = d - (act >= 0);
			if (!lgl->mt) {
				LOG(1, "empty clause during connection garbage collection phase");
				lglmt(lgl);
			}
		} else if (size == 1) {
			q = d - (act >= 0);
			LOG(1, "unit during garbage collection");
			lglunit(lgl, d[0]);
		} else if (size == 2) {
			q = d - (act >= 0);
			lglwchbin(lgl, d[0], d[1], red);
			lglwchbin(lgl, d[1], d[0], red);
		} else if (size == 3) {
			q = d - (act >= 0);
			lglwchtrn(lgl, d[0], d[1], d[2], red);
			lglwchtrn(lgl, d[1], d[0], d[2], red);
			lglwchtrn(lgl, d[2], d[0], d[1], red);
		} else {
			assert(size > 3);
			if (lgl->opts->sortlits.val)
				SORT(int, d, size, LGLCMPHASE);
			*q++ = 0;
			lidx = d - start;
			if (red) {
				assert(lidx <= MAXREDLIDX);
				lidx <<= GLUESHFT;
				assert(0 <= lidx);
				lidx |= glue;
			}
			(void)lglwchlrg(lgl, d[0], d[1], red, lidx);
			(void)lglwchlrg(lgl, d[1], d[0], red, lidx);
		}
		if (druplig && (collect || origsize != size)) {
			lglpushstk(lgl, &saved, 0);
			lgldrupligdelclsaux(lgl, saved.start);
		}
	}
	stk->top = q;
	lglrelstk(lgl, &saved);
}

static void
lglconsavedbin(LGL* lgl)
{
	int satisfied, eliminated, size, val0, val1, nunits = 0, nmt = 0;
	const int druplig = lgl->opts->druplig.val, *p;
	assert(lgl->rmredbintrn);
	for (p = lgl->saved.bin.start; p < lgl->saved.bin.top; p += 2) {
		assert(p + 1 < lgl->saved.bin.top);
		satisfied = eliminated = val0 = val1 = 0;
		if ((val0 = lglval(lgl, p[0])) > 0)
			satisfied = 1;
		else if ((val1 = lglval(lgl, p[1])) > 0)
			satisfied = 1;
		else if (lgliselim(lgl, p[0]))
			eliminated = 1;
		else if (lgliselim(lgl, p[1]))
			eliminated = 1;
		assert(!eliminated || lgl->eliminating || lgl->blocking);
		if (satisfied || eliminated)
			size = INT_MAX;
		else if (val0 < 0 && val1 < 0) {
			nmt++;
			size = 0;
			if (!lgl->mt) {
				LOG(1, "empty clause connecting saved binary clauses");
				lglmt(lgl);
			}
		} else if (!val0 && val1 < 0) {
			size = 1;
			LOG(1, "unit during connecting saved binary clauses");
			lglunit(lgl, p[0]);
			nunits++;
		} else if (val0 < 0 && !val1) {
			size = 1;
			LOG(1, "unit during connecting saved binary clauses");
			lglunit(lgl, p[1]);
			nunits++;
		} else {
			assert(!val0), assert(!val1);
			size = 2;
			lglwchbin(lgl, p[0], p[1], REDCS);
			lglwchbin(lgl, p[1], p[0], REDCS);
		}
		if (druplig && (satisfied || eliminated || size < 2))
			lgldrupligdelclsarg(lgl, p[0], p[1], 0);
	}
	lglprt(lgl,
		   2 - (nmt || nunits),
		   "[gc-%d] reconnected %d binary clauses (produced %d empty %d units)",
		   lgl->stats->gcs,
		   lglcntstk(&lgl->saved.bin) / 2,
		   nmt,
		   nunits);
	lglrelstk(lgl, &lgl->saved.bin);
}

static void
lglconsavedtrn(LGL* lgl)
{
	const int druplig = lgl->opts->druplig.val, *p;
	int satisfied, eliminated, size, val0, val1, val2;
	int nmt = 0, nunits = 0, nbins = 0;
	assert(lgl->rmredbintrn);
	for (p = lgl->saved.trn.start; p < lgl->saved.trn.top; p += 3) {
		assert(p + 1 < lgl->saved.trn.top);
		satisfied = eliminated = val0 = val1 = val2 = 0;
		if ((val0 = lglval(lgl, p[0])) > 0)
			satisfied = 1;
		else if ((val1 = lglval(lgl, p[1])) > 0)
			satisfied = 1;
		else if ((val2 = lglval(lgl, p[2])) > 0)
			satisfied = 1;
		else if (lgliselim(lgl, p[0]))
			eliminated = 1;
		else if (lgliselim(lgl, p[1]))
			eliminated = 1;
		else if (lgliselim(lgl, p[2]))
			eliminated = 1;
		assert(!eliminated || lgl->eliminating || lgl->blocking);
		if (satisfied || eliminated)
			size = INT_MAX;
		else if (val0 < 0 && val1 < 0 && val2 < 0) {
			nmt++;
			size = 0;
			if (!lgl->mt) {
				LOG(1, "empty clause connecting saved ternary clauses");
				lglmt(lgl);
			}
		} else if (!val0 && val1 < 0 && val2 < 0) {
			size = 1;
			LOG(1, "unit during connecing saved ternary clauses");
			lglunit(lgl, p[0]);
			nunits++;
		} else if (val0 < 0 && !val1 && val2 < 0) {
			size = 1;
			LOG(1, "unit during connecing saved ternary clauses");
			lglunit(lgl, p[1]);
			nunits++;
		} else if (val0 < 0 && val1 < 0 && !val2) {
			size = 1;
			LOG(1, "unit during connecing saved ternary clauses");
			lglunit(lgl, p[2]);
			nunits++;
		} else if (!val0 && !val1 && val2 < 0) {
			size = 2;
			nbins++;
			lglwchbin(lgl, p[0], p[1], REDCS);
			lglwchbin(lgl, p[1], p[0], REDCS);
			if (druplig)
				lgldrupligaddclsarg(lgl, REDCS, p[0], p[1], 0);
		} else if (!val0 && val1 < 0 && !val2) {
			size = 2;
			nbins++;
			lglwchbin(lgl, p[0], p[2], REDCS);
			lglwchbin(lgl, p[2], p[0], REDCS);
			if (druplig)
				lgldrupligaddclsarg(lgl, REDCS, p[0], p[2], 0);
		} else if (val0 < 0 && !val1 && !val2) {
			size = 2;
			nbins++;
			lglwchbin(lgl, p[1], p[2], REDCS);
			lglwchbin(lgl, p[2], p[1], REDCS);
			if (druplig)
				lgldrupligaddclsarg(lgl, REDCS, p[1], p[2], 0);
		} else {
			size = 3;
			assert(!val0), assert(!val1), assert(!val2);
			lglwchtrn(lgl, p[0], p[1], p[2], REDCS);
			lglwchtrn(lgl, p[1], p[0], p[2], REDCS);
			lglwchtrn(lgl, p[2], p[0], p[1], REDCS);
		}
		if (druplig && (satisfied || eliminated || size < 3))
			lgldrupligdelclsarg(lgl, p[0], p[1], p[2], 0);
	}
	lglprt(lgl,
		   2 - (nmt || nunits), // || nbins), // NOTE would be too verbose ...
		   "[gc-%d] reconnected %d ternary clauses "
		   " (produced %d empty %d units %d binary)",
		   lgl->stats->gcs,
		   lglcntstk(&lgl->saved.trn) / 3,
		   nmt,
		   nunits,
		   nbins);
	lglrelstk(lgl, &lgl->saved.trn);
}

static void
lglfullyconnected(LGL* lgl)
{
	if (!lgl->notfullyconnected)
		return;
	LOG(1, "switching to fully connected mode");
	lgl->notfullyconnected = 0;
}

static void
lglcon(LGL* lgl)
{
	int glue;
	for (glue = -1; glue < MAXGLUE; glue++)
		lglconnaux(lgl, glue);
	lglfullyconnected(lgl);
}

static void
lglconsaved(LGL* lgl)
{
	if (lgl->rmredbintrn) {
		lglconsavedbin(lgl);
		lglconsavedtrn(lgl);
		lgl->rmredbintrn = 0;
		if (!lgl->mt && !lglbcpcomplete(lgl)) {
			lglfullyconnected(lgl);
			if (!lglbcp(lgl)) {
				LOG(1, "empty clause generated propagating reconnected unit");
				lglmt(lgl);
			}
		}
	} else
		assert(lglmtstk(&lgl->saved.bin)), assert(lglmtstk(&lgl->saved.trn));
}

static void
lglcount(LGL* lgl)
{
	int idx, sign, lit, tag, blit, red, other, other2, glue, count;
	const int *p, *w, *c, *eow;
	HTS* hts;
	Stk* lir;
	lgl->stats->irr.clauses.cur = 0;
	lgl->stats->irr.lits.cur = 0;
	lgl->stats->red.bin = 0;
	lgl->stats->red.trn = 0;
	lgl->stats->red.lrg = 0;
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			if (!hts->offset)
				continue;
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				red = blit & REDCS;
				tag = blit & MASKCS;
				if (tag == LRGCS || tag == TRNCS)
					p++;
				if (tag == LRGCS)
					continue;
				assert(tag == BINCS || tag == TRNCS);
				other = blit >> RMSHFT;
				assert(abs(other) != abs(lit));
				if (abs(lit) >= abs(other))
					continue;
				assert(2 == BINCS && 3 == TRNCS);
				if (tag == TRNCS) {
					other2 = *p;
					assert(abs(other2) != abs(lit));
					assert(abs(other2) != abs(other));
					if (abs(lit) >= abs(other2))
						continue;
				}
				if (!red) {
					lgl->stats->irr.clauses.cur++;
					if (tag == BINCS)
						lgl->stats->irr.lits.cur += 2;
					else
						assert(tag == TRNCS), lgl->stats->irr.lits.cur += 3;
				} else if (tag == BINCS)
					lgl->stats->red.bin++;
				else
					assert(tag == TRNCS), lgl->stats->red.trn++;
			}
		}
	assert(lgl->stats->red.bin >= 0 && lgl->stats->red.trn >= 0);
	for (c = lgl->irr.start; c < lgl->irr.top; c = p + 1) {
		if (*(p = c) >= REMOVED)
			continue;
		while (*p)
			p++;
		lgl->stats->irr.lits.cur += p - c;
		lgl->stats->irr.clauses.cur++;
	}
	LOG(1, "counted %d irredundant clauses with %d literals", lgl->stats->irr.clauses.cur, lgl->stats->irr.lits.cur);
	for (glue = 0; glue < MAXGLUE; glue++) {
		lir = lgl->red + glue;
		count = 0;
		for (c = lir->start; c < lir->top; c++)
			if (!*c)
				count++;
		if (count)
			LOG(1, "counted %d redundant clauses with glue %d", count, glue);
		lgl->stats->red.lrg += count;
		lgl->stats->lir[glue].clauses = count;
	}
	assert(lgl->stats->red.lrg >= 0);
	if (lgl->stats->red.bin)
		LOG(1, "counted %d binary redundant clauses altogether", lgl->stats->red.bin);
	if (lgl->stats->red.trn)
		LOG(1, "counted %d ternary redundant clauses altogether", lgl->stats->red.trn);
	if (lgl->stats->red.lrg)
		LOG(1, "counted %d large redundant clauses altogether", lgl->stats->red.lrg);
}

static int
lglilit(int ulit)
{
	int res = ulit / 2;
	assert(res >= 1);
	if (ulit & 1)
		res = -res;
	return res;
}

static void
lgljwh(LGL* lgl)
{
	int idx, sign, lit, tag, blit, other, other2, red, size, glue;
	const int *p, *w, *eow, *c;
	Val val, tmp, tmp2;
	HTS* hts;
	Stk* s;
	Flt inc;
	CLN(lgl->jwh, 2 * lgl->nvars);
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			val = lglval(lgl, lit);
			if (val > 0)
				continue;
			hts = lglhts(lgl, lit);
			if (!hts->offset)
				continue;
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (tag == LRGCS)
					continue;
				red = blit & REDCS;
				if (!lgl->opts->jwhred.val && red)
					continue;
				if (lgl->opts->jwhred.val == 2 && !red)
					continue;
				other = blit >> RMSHFT;
				if (abs(other) < abs(lit))
					continue;
				tmp = lglval(lgl, other);
				if (tmp > 0)
					continue;
				if (tag == BINCS) {
					assert(!tmp);
					inc = lglflt(-2, 1);
					lglincjwh(lgl, lit, inc);
					lglincjwh(lgl, other, inc);
				} else {
					assert(tag == TRNCS);
					other2 = *p;
					if (abs(other2) < abs(lit))
						continue;
					tmp2 = lglval(lgl, other2);
					if (tmp2 > 0)
						continue;
					assert((val > 0) + (tmp > 0) + (tmp2 > 0) == 0);
					assert((val < 0) + (tmp < 0) + (tmp2 < 0) <= 1);
					size = 3 + val + tmp + tmp2;
					assert(2 <= size && size <= 3);
					inc = lglflt(-size, 1);
					if (!val)
						lglincjwh(lgl, lit, inc);
					if (!tmp)
						lglincjwh(lgl, other, inc);
					if (!tmp2)
						lglincjwh(lgl, other2, inc);
				}
			}
		}
	for (glue = -1; glue < MAXGLUE; glue++) {
		if (!lgl->opts->jwhred.val && glue >= 0)
			continue;
		if (lgl->opts->jwhred.val == 2 && glue < 0)
			continue;
		if (glue < 0)
			s = &lgl->irr;
		else
			s = &lgl->red[glue];
		for (c = s->start; c < s->top; c = p + 1) {
			if (*(p = c) >= NOTALIT)
				continue;
			val = -1;
			size = 0;
			while ((other = *p)) {
				tmp = lglval(lgl, other);
				if (tmp > val)
					val = tmp;
				if (!tmp)
					size++;
				p++;
			}
			if (val > 0)
				continue;
			inc = lglflt(-size, 1);
			for (p = c; (other = *p); p++)
				if (!lglval(lgl, other))
					lglincjwh(lgl, other, inc);
		}
	}
}

static int*
lglis(LGL* lgl)
{
	int idx, sign, lit, tag, blit, other, other2, red, *res;
	const int *p, *w, *eow, *c;
	Val val, tmp, tmp2;
	HTS* hts;
	Stk* s;
	NEW(res, 2 * lgl->nvars);
	res += lgl->nvars;
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			val = lglval(lgl, lit);
			if (val > 0)
				continue;
			hts = lglhts(lgl, lit);
			if (!hts->offset)
				continue;
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (tag == LRGCS)
					continue;
				red = blit & REDCS;
				if (red)
					continue;
				other = blit >> RMSHFT;
				if (abs(other) < abs(lit))
					continue;
				tmp = lglval(lgl, other);
				if (tmp > 0)
					continue;
				if (tag == BINCS) {
					assert(!tmp);
					res[lit]++, res[other]++;
				} else {
					assert(tag == TRNCS);
					other2 = *p;
					if (abs(other2) < abs(lit))
						continue;
					tmp2 = lglval(lgl, other2);
					if (tmp2 > 0)
						continue;
					assert((val > 0) + (tmp > 0) + (tmp2 > 0) == 0);
					assert((val < 0) + (tmp < 0) + (tmp2 < 0) <= 1);
					if (!val)
						res[lit]++;
					if (!tmp)
						res[other]++;
					if (!tmp2)
						res[other2]++;
				}
			}
		}
	s = &lgl->irr;
	for (c = s->start; c < s->top; c = p + 1) {
		p = c;
		if (*p >= NOTALIT)
			continue;
		val = -1;
		while ((other = *p)) {
			tmp = lglval(lgl, other);
			if (tmp > val)
				val = tmp;
			p++;
		}
		if (val > 0)
			continue;
		for (p = c; (other = *p); p++)
			if (!lglval(lgl, other))
				res[other]++;
	}
	return res;
}

static int
lglsetjwhbias(LGL* lgl, int idx)
{
	AVar* av = lglavar(lgl, idx);
	Flt pos, neg;
	int bias;
	idx = abs(idx);
	pos = lgl->jwh[lglulit(idx)];
	neg = lgl->jwh[lglulit(-idx)];
	if (av->phase)
		return av->phase;
	bias = (pos > neg) ? 1 : -1;
	if (av->bias == bias)
		return bias;
	av->bias = bias;
	lgl->stats->phase.set++;
	if (bias > 0)
		lgl->stats->phase.pos++;
	else
		lgl->stats->phase.neg++;
	LOG(1, "JW bias %d (pos %s, neg %s)", bias * idx, lglflt2str(lgl, pos), lglflt2str(lgl, neg));
	return bias;
}

static void
lglsetbias(LGL* lgl, int idx, int bias)
{
	AVar* av = lglavar(lgl, idx);
	assert(abs(bias) == 1);
	if (av->phase)
		return;
	av->bias = bias;
	lgl->stats->phase.set++;
	if (bias > 0)
		lgl->stats->phase.pos++;
	else
		lgl->stats->phase.neg++;
	LOG(1, "set bias %d explicitly", bias * idx);
}

static void
lglsetallphases(LGL* lgl)
{
	int res = 1, idx;
	for (idx = 2; res && idx < lgl->nvars; idx++)
		res = (!lglisfree(lgl, idx) || lglavar(lgl, idx)->phase);
	lgl->allphaseset = res;
}

static void
lglflushphases(LGL* lgl)
{
	int idx, flushed = 0;
	AVar* av;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		av = lglavar(lgl, idx);
		av->phase = 0;
		flushed++;
	}
	lglprt(lgl, 1, "[flushphases] %d phases reset", flushed);
	lgl->allphaseset = !flushed;
	lgl->flushphases = 0;
}

static int
lgluckyphase(LGL* lgl)
{
	int pos = 0, neg = 0, lit, idx, count = 0, res = 0;
	int negrest, posrest;
	const int *p, *c;
	for (c = lgl->irr.start; c < lgl->irr.top; c = p + 1) {
		int foundpos = 0, foundneg = 0;
		if (*(p = c) >= NOTALIT)
			continue;
		while ((lit = *p)) {
			if (lit > 0)
				foundpos = 1;
			if (lit < 0)
				foundneg = 1;
			p++;
		}
		if (foundpos)
			pos++;
		if (foundneg)
			neg++;
		count++;
	}
	for (idx = 2; idx < lgl->nvars; idx++) {
		HTS* hts = lglhts(lgl, idx);
		const int *p, *w, *eow;
		w = lglhts2wchs(lgl, hts);
		eow = w + hts->count;
		for (p = w; p < eow; p++) {
			int blit = *p, red, other;
			int tag = blit & MASKCS;
			if (tag == TRNCS || tag == LRGCS)
				p++;
			if (tag == LRGCS)
				continue;
			red = blit & REDCS;
			if (red)
				continue;
			other = blit >> RMSHFT;
			if (abs(other) < idx)
				continue;
			if (tag == TRNCS) {
				int other2 = *p;
				if (abs(other2) < idx)
					continue;
				if (other < 0 || other2 < 0)
					neg++;
			} else if (other < 0)
				neg++;
			count++;
			pos++;
		}
		hts = lglhts(lgl, -idx);
		w = lglhts2wchs(lgl, hts);
		eow = w + hts->count;
		for (p = w; p < eow; p++) {
			int blit = *p, red, other;
			int tag = blit & MASKCS;
			if (tag == TRNCS || tag == LRGCS)
				p++;
			if (tag == LRGCS)
				continue;
			red = blit & REDCS;
			if (red)
				continue;
			other = blit >> RMSHFT;
			if (abs(other) < idx)
				continue;
			if (tag == TRNCS) {
				int other2 = *p;
				if (abs(other2) < idx)
					continue;
				if (other > 0 || other2 > 0)
					pos++;
			} else if (other > 0)
				pos++;
			count++;
			neg++;
		}
	}
	assert(count >= neg);
	negrest = count - neg;
	assert(count >= pos);
	posrest = count - pos;
	assert(count == lgl->stats->irr.clauses.cur);
	lglprt(lgl,
		   1,
		   "[phase-count-%d] %d negative %.1f%% of %d except %d %.1f%%",
		   lgl->stats->phase.count,
		   neg,
		   lglpcnt(neg, count),
		   count,
		   negrest,
		   lglpcnt(negrest, count));
	lglprt(lgl,
		   1,
		   "[phase-count-%d] %d positive %.1f%% of %d except %d %.1f%%",
		   lgl->stats->phase.count,
		   pos,
		   lglpcnt(pos, count),
		   count,
		   posrest,
		   lglpcnt(posrest, count));
	if (neg >= count) {
		res = -1;
		lglprt(lgl, 1, "[phase-count-%d] all clauses contain a negative literal", lgl->stats->phase.count);
	} else if (pos >= count) {
		res = 1;
		lglprt(lgl, 1, "[phase-count-%d] all clauses contain a positive literal", lgl->stats->phase.count);
	} else if (lgl->stats->phase.count <= lgl->opts->phaseluckmaxround.val) {
		int lim = lgl->opts->phaselucklim.val * (count / 1000);
		int fac = lgl->opts->phaseluckfactor.val;
		if (negrest <= lim && negrest <= posrest && posrest / fac >= negrest / 100) {
			res = -1;
			lglprt(lgl,
				   1,
				   "[phase-count-%d] less non-negative clauses %d than limit %d (%.1f%%)",
				   lgl->stats->phase.count,
				   negrest,
				   lim,
				   lgl->opts->phaselucklim.val / 10.0);
			lglprt(lgl,
				   1,
				   "[phase-count-%d] non-pos/non-neg = %d/%d = %.2f >= %.2f = %d/100",
				   lgl->stats->phase.count,
				   posrest,
				   negrest,
				   posrest / (double)negrest,
				   fac / 100.0,
				   fac);
		} else if (posrest <= lim && posrest <= negrest && negrest / fac >= posrest / 100) {
			res = 1;
			lglprt(lgl,
				   1,
				   "[phase-count-%d] less non-positive clauses %d than limit %d (%.1f%%)",
				   lgl->stats->phase.count,
				   posrest,
				   lim,
				   lgl->opts->phaselucklim.val / 1000.0);
			lglprt(lgl,
				   1,
				   "[phase-count-%d] non-neg/non-pos = %d/%d = %.2f >= %.2f = %d/100",
				   lgl->stats->phase.count,
				   negrest,
				   posrest,
				   negrest / (double)posrest,
				   fac / 100.0,
				   fac);
		}
	} else
		lglprt(lgl, 1, "[phase-count-%d] skipping relative phase luck test", lgl->stats->phase.count);
	if (res)
		lglprt(lgl, 1, "[lucky-phase-%d] forcing lucky phase %d", lgl->stats->phase.count, res);
	return res;
}

static void
lglphase(LGL* lgl)
{
	int64_t set = lgl->stats->phase.set;
	int64_t pos = lgl->stats->phase.pos;
	int64_t neg = lgl->stats->phase.neg;
	int idx, lucky;
	lglstart(lgl, &lgl->times->phase);
	if (lgl->flushphases)
		lglflushphases(lgl);
	if (lgl->opts->phase.val)
		goto DONE;
	lglsetallphases(lgl);
	if (lgl->allphaseset)
		goto DONE;
	lgl->stats->phase.count++;
	lgljwh(lgl);
	if ((lucky = lgluckyphase(lgl)))
		for (idx = 2; idx < lgl->nvars; idx++)
			lglsetbias(lgl, idx, lucky);
	else
		for (idx = 2; idx < lgl->nvars; idx++)
			lglsetjwhbias(lgl, idx);
	set = lgl->stats->phase.set - set;
	pos = lgl->stats->phase.pos - pos;
	neg = lgl->stats->phase.neg - neg;
	lglprt(lgl,
		   1,
		   "[phase-%d] phase bias: %lld positive %.0f%%, %lld negative %.0f%%",
		   lgl->stats->phase.count,
		   (LGLL)pos,
		   lglpcnt(pos, set),
		   (LGLL)neg,
		   lglpcnt(neg, set));
DONE:
	lglstop(lgl);
}

static int
lglmaplit(int* map, int lit)
{
	return map[abs(lit)] * lglsgn(lit);
}

static void
lglmapstk(LGL* lgl, int* map, Stk* lits)
{
	int *p, *eol;
	eol = lits->top;
	for (p = lits->start; p < eol; p++)
		*p = lglmaplit(map, *p);
	(void)lgl;
}

static void
lglmapqueue(LGL* lgl, int* map)
{
	int idx, found, *q, src, dst;
	const int *p, *start;
	QVar* qv;
	for (idx = 2; idx < lgl->nvars; idx++) {
		qv = lglqvar(lgl, idx);
		if (!qv->enqueued)
			continue;
		qv->pos = -1;
	}
	found = 0;
	start = q = lgl->queue.stk.start;
	for (p = q; p < lgl->queue.stk.top; p++) {
		if (!(src = *p))
			continue;
		assert(1 < src);
		dst = lglmaplit(map, src);
		if (dst < 0)
			dst = -dst;
		if (dst <= 1)
			continue;
		qv = lglqvar(lgl, dst);
		if (!qv->enqueued)
			continue;
		if (qv->pos >= 0)
			continue;
		qv->pos = (q - start);
		assert(qv->pos >= 0);
		if (!found && lgl->queue.next >= (p - start))
			found = 1, lgl->queue.next = qv->pos;
		*q++ = dst;
	}
	for (idx = 2; idx < lgl->nvars; idx++) {
		qv = lglqvar(lgl, idx);
		if (!qv->enqueued)
			continue;
		if (qv->pos >= 0)
			continue;
		assert(q < lgl->queue.stk.top);
		qv->pos = (q - start);
		assert(qv->pos >= 0);
		*q++ = idx;
	}
	lgl->queue.stk.top = q;
	lglfitstk(lgl, &lgl->queue.stk);
	if (!found)
		lgl->queue.next = lglcntstk(&lgl->queue.stk) - 1;
	lgl->queue.mt = 0;
}

static void
lglmapglue(LGL* lgl, int* map, Stk* lits)
{
	int *p, *eol;
	eol = lits->top;
	for (p = lits->start; p < eol; p++)
		if (!lglisact(*p))
			*p = lglmaplit(map, *p);
	(void)lgl;
}

static void
lglmaplits(LGL* lgl, int* map)
{
	int glue;
	lglmapstk(lgl, map, &lgl->irr);
	for (glue = 0; glue < MAXGLUE; glue++)
		lglmapglue(lgl, map, &lgl->red[glue]);
}

static void
lglmapvars(LGL* lgl, int* map, int nvars)
{
	int i, oldnvars = lgl->nvars, sign, udst, idst, usrc, isrc;
	DVar* dvars;
	QVar* qvars;
	AVar* avars;
	Val* vals;
	int* i2e;
	Flt* jwh;

	if (nvars > 2)
		assert(nvars <= oldnvars);
	else
		nvars = 0;

	DEL(lgl->doms, 2 * lgl->szvars);
	NEW(lgl->doms, 2 * nvars);

	NEW(vals, nvars);
	for (i = 2; i < oldnvars; i++)
		if (lglisfree(lgl, i))
			vals[map[i]] = lgl->vals[i];
	DEL(lgl->vals, lgl->szvars);
	lgl->vals = vals;

	NEW(i2e, nvars);
	for (i = 2; i < oldnvars; i++)
		if (lglisfree(lgl, i))
			i2e[map[i]] = lgl->i2e[i];
	DEL(lgl->i2e, lgl->szvars);
	lgl->i2e = i2e;

	NEW(dvars, nvars);
	for (i = 2; i < oldnvars; i++)
		if (lglisfree(lgl, i))
			dvars[map[i]] = lgl->dvars[i];
	DEL(lgl->dvars, lgl->szvars);
	lgl->dvars = dvars;

	NEW(qvars, nvars);
	for (i = 2; i < oldnvars; i++)
		if (lglisfree(lgl, i))
			qvars[map[i]] = lgl->qvars[i];
	DEL(lgl->qvars, lgl->szvars);
	lgl->qvars = qvars;

	NEW(jwh, 2 * nvars);
	for (i = 2; i < oldnvars; i++)
		if (lglisfree(lgl, i))
			for (sign = -1; sign <= 1; sign += 2) {
				isrc = sign * i;
				idst = sign * map[i];
				usrc = lglulit(isrc);
				udst = lglulit(idst);
				jwh[udst] = lgladdflt(jwh[udst], lgl->jwh[usrc]);
			}
	DEL(lgl->jwh, 2 * lgl->szvars);
	lgl->jwh = jwh;

	NEW(avars, nvars);
	for (i = 2; i < oldnvars; i++)
		if (lglisfree(lgl, i))
			avars[map[i]] = lgl->avars[i];
	DEL(lgl->avars, lgl->szvars);
	lgl->avars = avars; // Last since 'lglisfree' depends on it !!!

	lgl->nvars = lgl->szvars = nvars;
	lgl->stats->fixed.current = 0;
}

static void
lglmaphts(LGL* lgl, int* map)
{
	int idx, sign, lit, *w, *eow, *p, other, other2, blit, tag, red;
	int newblit, newother, newother2;
	HTS* hts;
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			if (!hts->count)
				continue;
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				assert(tag == BINCS || tag == TRNCS || tag == LRGCS);
				red = blit & REDCS;
				other = blit >> RMSHFT;
				newother = lglmaplit(map, other);
				newblit = RMSHFTLIT(newother) | tag | red;
				*p = newblit;
				if (tag == BINCS)
					continue;
				other2 = *++p;
				if (tag == LRGCS)
					continue;
				assert(tag == TRNCS);
				newother2 = lglmaplit(map, other2);
				*p = newother2;
			}
		}
}

static void
lglmaptrail(LGL* lgl, int* map)
{
	int *p, *q, src, dst;
	for (p = lgl->trail.start; p < lgl->trail.top; p++)
		if (lglevel(lgl, *p) > 0)
			break;
	for (q = lgl->trail.start; p < lgl->trail.top; p++) {
		src = *p;
		assert(lglevel(lgl, src) > 0);
		dst = lglmaplit(map, src);
		*q++ = dst;
	}
	lgl->trail.top = q;
	lgl->flushed = lgl->next2 = lgl->next = lglcntstk(&lgl->trail);
}

static int
lglptrjmp(int* repr, int max, int start)
{
#ifndef NDEBUG
	int prev = 0, count = 0;
#endif
	int next, idx, res, sgn, tmp;
	assert(repr);
	next = start;
	do {
		res = next;
		idx = abs(res);
		assert(idx <= max);
		sgn = lglsgn(res);
		next = repr[idx];
		next *= sgn;
#ifndef NDEBUG
		if (prev || next)
			assert(prev != next);
		prev = res;
		assert(count <= max);
#endif
	} while (next);
	tmp = start;
	while (tmp != res) {
		idx = abs(tmp), sgn = lglsgn(tmp);
		next = repr[idx] * sgn;
		repr[idx] = sgn * res;
		tmp = next;
	}
	(void)max;
	return res;
}

static int
lglirepr(LGL* lgl, int lit)
{
	assert(lgl->repr);
	return lglptrjmp(lgl->repr, lgl->nvars - 1, lit);
}

static void
lglmapext(LGL* lgl, int* map)
{
	int eidx, ilit, mlit;
	Ext* ext;
	for (eidx = 1; eidx <= lgl->maxext; eidx++)
		(void)lglerepr(lgl, eidx);
	for (eidx = 1; eidx <= lgl->maxext; eidx++) {
		ext = lgl->ext + eidx;
		if (!ext->imported)
			continue;
		if (ext->equiv) {
			LOG(3, "mapping external %d to equivalent external %d", eidx, ext->repr);
			continue;
		}
		ilit = ext->repr;
		mlit = lglmaplit(map, ilit);
		LOG(3, "mapping external %d to internal %d", eidx, mlit);
		ext->repr = mlit;
	}
}

static void
lglsignedmark(LGL* lgl, int lit)
{
	AVar* av = lglavar(lgl, lit);
	int bit = 1 << (lit < 0);
	if (av->mark & bit)
		return;
	av->mark |= bit;
}

static void
lglsignedunmark(LGL* lgl, int lit)
{
	AVar* av = lglavar(lgl, lit);
	int bit = 1 << (lit < 0);
	if (!(av->mark & bit))
		return;
	av->mark &= ~bit;
}

static int
lglsignedmarked(LGL* lgl, int lit)
{
	AVar* av = lglavar(lgl, lit);
	int bit = 1 << (lit < 0);
	return av->mark & bit;
}

static void
lglmapass(LGL* lgl, int* map)
{
	int *p, *q, iass, mass, flushed;
	unsigned bit;
	AVar* av;
	if (abs(lgl->failed) != 1)
		lgl->failed = lglmaplit(map, lgl->failed);
	for (p = q = lgl->assume.start; p < lgl->assume.top; p++) {
		iass = *p;
		mass = lglmaplit(map, iass);
		if (mass == 1)
			continue;
		if (mass == -1) {
			if (lgl->failed != -1) {
#ifndef NDEBUG
				int* r;
				for (r = lgl->eassume.start; r < lgl->eassume.top; r++)
					if (lglimport(lgl, *r) == -1)
						break;
				assert(r < lgl->eassume.top);
#endif
				LOG(2, "enforcing a failed assumption");
				lgl->failed = -1;
			}
			continue;
		}
		LOG(2, "mapping previous internal assumption %d to %d", iass, mass);
		av = lglavar(lgl, mass);
		bit = 1u << (mass < 0);
		if (!(av->assumed & bit)) {
			LOG(2, "assuming new representative %d instead of %d", mass, iass);
			av->assumed |= bit;
		}
		*q++ = mass;
	}
	lgl->assume.top = q;
	flushed = 0;
	for (p = q = lgl->assume.start; p < lgl->assume.top; p++) {
		iass = *p;
		if (lglsignedmarked(lgl, iass)) {
			flushed++;
			continue;
		}
		lglsignedmark(lgl, iass);
		*q++ = iass;
	}
	lgl->assume.top = q;
	for (p = lgl->assume.start; p < lgl->assume.top; p++) {
		iass = *p;
		assert(lglsignedmarked(lgl, iass));
		lglsignedunmark(lgl, iass);
	}
	if (flushed)
		LOG(2, "flushed %d duplicated internal assumptions", flushed);
}

static void
lglmaplkhdscore(LGL* lgl, int* map, int oldnvars)
{
	LKHD *oldlkhdscore = lgl->tlk->lkhd, score;
	int idx, src, sign, dst;
	NEW(lgl->tlk->lkhd, 2 * lgl->nvars);
	lgl->tlk->lkhd += lgl->nvars;
	for (idx = oldnvars - 1; idx >= 2; idx--) {
		for (sign = -1; sign <= 1; sign += 2) {
			src = sign * idx;
			dst = lglmaplit(map, src);
			if (abs(dst) <= 1)
				continue;
			score = oldlkhdscore[src];
			LOG(2, "mapping look-ahead score %s of %d as score of %d", lglflt2str(lgl, score), src, dst);
			lgl->tlk->lkhd[dst] = score;
		}
	}
	oldlkhdscore -= oldnvars;
	DEL(oldlkhdscore, 2 * oldnvars);
}

static int
lglmapsize(LGL* lgl)
{
	int size = 0, idx;
	for (idx = 2; idx < lgl->nvars; idx++)
		if (lglisfree(lgl, idx))
			size++;
	LOG(1, "mapping %d remaining variables", size);
	return size;
}

static void
lglmapnonequiv(LGL* lgl, int* map, int size)
{
	int count = 0, idx;
	AVar* av;
	Val val;
	map[0] = 0, map[1] = 1;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (map[idx])
			continue;
		av = lglavar(lgl, idx);
		if (av->type == FREEVAR) {
			assert(idx > 0);
			if (map[idx]) {
				assert(map[idx] < idx);
				continue;
			}
			LOG(3, "mapping free %d to %d", idx, count + 2);
			map[idx] = count + 2;
			count++;
		} else if (av->type == EQUIVAR) {
			assert(lgl->repr);
			assert(!map[idx]);
		} else if (av->type == FIXEDVAR) {
			val = lgl->vals[idx];
			assert(val);
			LOG(3, "mapping assigned %d to %d", idx, (int)val);
			map[idx] = val;
		} else {
			assert(av->type == ELIMVAR);
			assert(!lglifrozen(lgl, idx));
			map[idx] = 0;
		}
	}
	assert(count == size);
	(void)size;
}

static void
lglmapequiv(LGL* lgl, int* map)
{
	int idx, repr, dst;
	AVar* av;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (map[idx])
			continue;
		av = lglavar(lgl, idx);
		if (av->type == ELIMVAR)
			continue;
		assert(av->type == EQUIVAR);
		assert(lgl->repr);
		assert(!map[idx]);
		repr = lglirepr(lgl, idx);
		assert(repr != idx);
		assert(map[abs(repr)]);
		dst = lglmaplit(map, repr);
		LOG(3, "mapping equivalent %d to %d", idx, dst);
		map[idx] = dst;
	}
}

static int64_t
lglsteps(LGL* lgl)
{
	int64_t res = lgl->stats->steps;
#ifndef NDEBUG
	// Note, this would be bulky and slow ...
	int64_t steps = 0;
	steps += lgl->stats->bca.steps;
	steps += lgl->stats->blk.steps;
	steps += lgl->stats->card.steps;
	steps += lgl->stats->cce.steps;
	steps += lgl->stats->elm.copies;
	steps += lgl->stats->elm.resolutions;
	steps += lgl->stats->elm.steps;
	steps += lgl->stats->gauss.steps.elim;
	steps += lgl->stats->gauss.steps.extr;
	steps += lgl->stats->prb.basic.steps;
	steps += lgl->stats->prb.simple.steps;
	steps += lgl->stats->prb.treelook.steps;
	steps += lgl->stats->props.lkhd;
	steps += lgl->stats->props.search;
	steps += lgl->stats->props.simp;
	steps += lgl->stats->sweep.steps;
	steps += lgl->stats->trd.steps;
	steps += lgl->stats->ternres.steps;
	steps += lgl->stats->unhd.steps;
	assert(steps == res);
#endif
	return res;
}

static void
lgltrep(LGL* lgl)
{
	int64_t steps, i;
	double t;
	if (!lgl->opts->trep.val)
		return;
	if (lgl->opts->verbose.val)
		return;
	steps = lglsteps(lgl);
	if (steps < lgl->limits->trep.steps)
		return;
	lgl->limits->trep.steps += lgl->opts->trepint.val;
	t = lgl->opts->abstime.val ? lglgetime(lgl) : lglsec(lgl);
	if (t < lgl->limits->trep.time)
		return;
	lglrep(lgl, 0, (lgl->simp ? 'P' : 'S'));
	i = lgl->limits->trep.time;
	do {
		if (!i)
			i = 1;
		else if (i == 1)
			i = 2;
		else if (i == 2)
			i = 5;
		else if (i < 10)
			i = 10;
		else if (i < 60)
			i += 10;
		else if (i < 300)
			i += 60;
		else if (i < 900)
			i += 300;
		else if (i < 7200)
			i += 900;
		else
			i += 3600;
	} while (i < t);
	lgl->limits->trep.time = i;
}

static int
lglterminate(LGL* lgl)
{
	int64_t steps;
	int res;
	lgltrep(lgl);
	if (!lgl->cbs)
		return 0;
	if (!lgl->cbs->term.fun)
		return 0;
	if (lgl->cbs->term.done)
		return 1;
	steps = lglsteps(lgl);
	if (steps < lgl->limits->term.steps)
		return 0;
	res = lgl->cbs->term.fun(lgl->cbs->term.state);
	if (res)
		lgl->cbs->term.done = res;
	else
		lgl->limits->term.steps = steps + lgl->opts->termint.val;
	return res;
}

static void
lglmap(LGL* lgl)
{
	int size, *map, oldnvars, mapsize;
#ifndef NDEBUG
	lglrelstk(lgl, &lgl->prevclause);
#endif
	lglrelstk(lgl, &lgl->promote);
	assert(!lgl->level);
	lgldreschedule(lgl);
	size = lglmapsize(lgl);
	oldnvars = lgl->nvars;
	mapsize = lglmax(oldnvars, 2);
	NEW(map, mapsize);
	lglmapnonequiv(lgl, map, size);
	lglmapequiv(lgl, map);
	lglmaptrail(lgl, map);
	lglmapvars(lgl, map, size + 2);
	lglmaplits(lgl, map);
	lglmapstk(lgl, map, &lgl->dsched);
	lglmapqueue(lgl, map);
	lglmapext(lgl, map);
	lglmapass(lgl, map);
	if (lgl->treelooking && lgl->tlk && lgl->tlk->lkhd)
		lglmaplkhdscore(lgl, map, oldnvars);
	assert(lglmtstk(&lgl->clause));
	lglmaphts(lgl, map);
	DEL(map, mapsize);
	if (lgl->repr)
		DEL(lgl->repr, oldnvars);
	lgl->unassigned = size;
	lgldreschedule(lgl);
#ifndef NLGLDRUPLIG
	lgl->drupligunit = 0;
#endif
}

static int
lglgcnotnecessary(LGL* lgl)
{
	if (lgl->forcegc)
		return 0;
	if (lgl->notfullyconnected)
		return 0;
	return lgl->stats->fixed.sum <= lgl->limits->gc.fixed;
}

static void
lglcompact(LGL* lgl)
{
	int glue;
	lglfitstk(lgl, &lgl->assume);
#ifndef NDEBUG
	lglfitstk(lgl, &lgl->prevclause);
#endif
	lglfitstk(lgl, &lgl->clause);
	lglfitstk(lgl, &lgl->eclause);
	lglfitstk(lgl, &lgl->dsched);
	lglfitstk(lgl, &lgl->queue.stk);
	lglfitstk(lgl, &lgl->eassume);
	lglfitstk(lgl, &lgl->extend);
	lglfitstk(lgl, &lgl->learned);
	lglfitstk(lgl, &lgl->frames);
	lglfitstk(lgl, &lgl->promote);
#ifndef NCHKSOL
	lglfitstk(lgl, &lgl->orig);
#endif
	lglfitstk(lgl, &lgl->trail);
	lgldefrag(lgl);
	lglfitstk(lgl, &lgl->wchs->stk);

	lglfitstk(lgl, &lgl->irr);
	for (glue = 0; glue <= MAXGLUE; glue++)
		lglfitlir(lgl, lgl->red + glue);
	lglrelstk(lgl, &lgl->lcaseen);
	lglrelstk(lgl, &lgl->resolvent);
	lglrelstk(lgl, &lgl->minstk);
	lglrelstk(lgl, &lgl->poisoned);
	lglrelstk(lgl, &lgl->seen);
	lglrelstk(lgl, &lgl->esched);
	lglrelstk(lgl, &lgl->saved.bin);
	lglrelstk(lgl, &lgl->saved.trn);
}

static void
lglgc(LGL* lgl)
{
	if (lgl->mt)
		return;
	lglchkred(lgl);
	if (lglgcnotnecessary(lgl))
		return;
	lglstart(lgl, &lgl->times->gc);
	lglchkbcpclean(lgl, "gc");
	lglrep(lgl, 2, 'g');
	lgl->stats->gcs++;
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	lglconsaved(lgl);
	do { // Note, need to go into loop even if 'lgl->mt'!
		lgldis(lgl);
		lglcon(lgl);
		if (lgl->mt)
			break;
		if (lglbcpcomplete(lgl))
			break;
		if (lglbcp(lgl))
			continue;
		assert(!lgl->mt);
		LOG(1, "empty clause after propagating garbage collection unit");
		lglmt(lgl);
	} while (!lgl->mt);
	lglcount(lgl);
	lglmap(lgl);

	lglcompact(lgl);

	lgl->limits->gc.fixed = lgl->stats->fixed.sum;

	lglchkred(lgl);
	lglrep(lgl, 2, 'c');
	lglstop(lgl);
}

static int
lgltopgc(LGL* lgl)
{
	if (lgl->mt)
		return 0;
	assert(!lgl->forcegc && !lgl->allowforce);
	lgl->forcegc = lgl->allowforce = 1;
	lglgc(lgl);
	assert(lgl->forcegc && lgl->allowforce);
	lgl->forcegc = lgl->allowforce = 0;
	return !lgl->mt;
}

static int
lglrandec(LGL* lgl)
{
	unsigned size, pos, start, delta;
	int lit;
	lgl->limits->randec = lgl->stats->decisions;
	lgl->limits->randec += lgl->opts->randecint.val / 2;
	lgl->limits->randec += lglrand(lgl) % lgl->opts->randecint.val;
	assert(lgl->nvars > 2);
	size = lgl->nvars - 2;
	if (!size)
		return 0;
	pos = start = lglrand(lgl) % size;
	lit = 2 + pos;
	assert(2 <= lit && lit < lgl->nvars);
	if (lglval(lgl, lit)) {
		delta = lglrand(lgl) % size;
		if (size == 1)
			return 0;
		if (!delta)
			delta++;
		while (lglgcd(delta, size) != 1)
			if (++delta == size)
				delta = 1;
		do {
			pos += delta;
			if (pos >= size)
				pos -= size;
			if (pos == start)
				return 0;
			lit = pos + 2;
			assert(2 <= lit && lit < lgl->nvars);
		} while (lglval(lgl, lit));
	}
	lgl->stats->randecs++;
	return lit;
}

static int
lgladecide(LGL* lgl)
{
	int res, val;
	while (lgl->assumed < (int)lglcntstk(&lgl->assume)) {
		res = lglpeek(&lgl->assume, lgl->assumed);
		val = lglcval(lgl, res);
		if (val > 0)
			LOG(3, "assumption %d already satisfied", res);
		lgl->assumed++;
		LOG(3, "new assumption queue level %d", lgl->assumed);
		assert(val >= 0);
		if (!val)
			return res;
	}
	return 0;
}

static int
lgldefphase(LGL* lgl, int idx)
{
	int bias, res;
	AVar* av;
	assert(idx > 0);
	av = lglavar(lgl, idx);
	bias = lgl->opts->phase.val;
	if (!bias)
		bias = av->bias;
	if (!bias)
		bias = lglsetjwhbias(lgl, idx);
	if (lgl->opts->phasesave.val) {
		if (!(res = av->phase))
			res = av->phase = bias;
		else if (lgl->opts->phasesave.val < 0)
			res = -res;
	} else
		res = bias;
	return res;
}

static int
lgldecidephase(LGL* lgl, int lit)
{
	int res = abs(lit);
	AVar* av = lglavar(lgl, lit);
	if (av->fase)
		return av->fase * res;
	if (lgldefphase(lgl, res) <= 0)
		res = -res;
	if (lgl->opts->randphase.val && lgl->limits->randphase <= lgl->stats->decisions) {
		LOG(1, "flipping phase due to random phases enabled");
		lgl->limits->randphase = lgl->stats->decisions;
		lgl->limits->randphase += lgl->opts->randphaseint.val / 2;
		lgl->limits->randphase += lglrand(lgl) % lgl->opts->randphaseint.val;
		lgl->stats->randphases++;
		res = -res;
	}
	return res;
}

static int
lglhasbins(LGL* lgl, int lit)
{
	int blit, tag, other, other2, val, val2, implied;
	const int *p, *w, *eos, *q;
	HTS* hts;
	assert(!lgl->level);
	assert(lglisfree(lgl, lit));
	hts = lglhts(lgl, lit);
	w = lglhts2wchs(lgl, hts);
	eos = w + hts->count;
	for (p = w; p < eos; p++) {
		if (lgl->treelooking)
			INCSTEPS(prb.treelook.steps);
		blit = *p;
		tag = blit & MASKCS;
		if (tag == BINCS) {
			other = blit >> RMSHFT;
			val = lglval(lgl, other);
			assert(val >= 0);
			if (!val)
				return 1;
		} else if (tag == TRNCS) {
			other = blit >> RMSHFT;
			other2 = *++p;
			val = lglval(lgl, other);
			val2 = lglval(lgl, other2);
			assert(val >= 0 || val2 >= 0);
			if (val > 0 || val2 > 0)
				continue;
			if (!val && val2 < 0)
				return 1;
			if (val < 0 && !val2)
				return 1;
		} else {
			assert(tag == LRGCS);
			q = lglidx2lits(lgl, (blit & REDCS), *++p);
			implied = 0;
			while ((other = *q++)) {
				if (other == lit)
					continue;
				val = lglval(lgl, other);
				if (val > 0)
					break;
				if (val < 0)
					continue;
				if (implied)
					break;
				implied = other;
			}
			if (other)
				continue;
			if (implied)
				return 1;
		}
	}
	return 0;
}

static int
lglprunedecide(LGL*);

static int
lgldecide(LGL* lgl)
{
	int lit;
	lglchkbcpclean(lgl, "decide");
	if (!lgl->unassigned)
		return 0;
	if ((lit = lgladecide(lgl))) {
		LOG(2, "using assumption %d as decision", lit);
		lgl->alevel = lgl->level + 1;
		LOG(2, "new assumption decision level %d", lgl->alevel);
	} else if (!(lit = lglprunedecide(lgl))) {
		if (lgl->stats->stability.level > 0 && lgl->stats->stability.level <= lgl->level)
			lglupdstab(lgl);
		if (lgl->opts->randec.val && lgl->limits->randec <= lgl->stats->decisions) {
			lit = lglrandec(lgl);
			lit = lgldecidephase(lgl, lit);
			LOG(2, "random decision %d", lit);
		} else {
			lit = lglnextdecision(lgl, 1);
			lit = lgldecidephase(lgl, lit);
		}
	}
	if (lit && lit != INT_MIN)
		lgldassume(lgl, lit);
	return 1;
}

static void
lgldcpdis(LGL* lgl)
{
	int idx, sign, lit, tag, blit, red, other, other2, i;
	const int *w, *p, *eow;
	Val val;
	HTS* hts;
	Stk* s;
	NEW(lgl->dis, 1);
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			if (!hts->offset)
				continue;
			assert(hts->count > 0);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			hts->count = hts->offset = 0;
			val = lglval(lgl, lit);
			if (val > 0)
				continue;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (tag == LRGCS)
					continue;
				other = blit >> RMSHFT;
				if (abs(other) < idx)
					continue;
				val = lglval(lgl, other);
				if (val > 0)
					continue;
				red = blit & REDCS;
				if (red && !lglisfree(lgl, other))
					continue;
				if (tag == BINCS) {
					s = red ? &lgl->dis->red.bin : &lgl->dis->irr.bin;
				} else {
					assert(tag == TRNCS);
					other2 = *p;
					if (abs(other2) < idx)
						continue;
					val = lglval(lgl, other2);
					if (val > 0)
						continue;
					if (red && !lglisfree(lgl, other2))
						continue;
					s = red ? &lgl->dis->red.trn : &lgl->dis->irr.trn;
					lglpushstk(lgl, s, other2);
				}
				lglpushstk(lgl, s, other);
				lglpushstk(lgl, s, lit);
				lglpushstk(lgl, s, 0);
			}
		}
	lglrststk(&lgl->wchs->stk, 2);
	lgl->wchs->stk.top[-1] = (int)UINT_MAX;
	for (i = 0; i < MAXLDFW; i++)
		lgl->wchs->start[i] = UINT_MAX;
	lgl->wchs->free = 0;
	lglrelstk(lgl, &lgl->learned);
}

static void
lgldcpclnstk(LGL* lgl, int red, Stk* s)
{
	int oldsz, newsz, lit, mark, satisfied, repr, act, changed;
	const int druplig = lgl->opts->druplig.val;
	const int *p, *c, *eos = s->top;
	int *start, *q, *r, *d;
	Stk *t, saved;
	Val val;
	CLR(saved);
	q = start = s->start;
	for (c = q; c < eos; c = p + 1) {
		act = *c;
		if (act == REMOVED) {
			for (p = c + 1; p < eos && *p == REMOVED; p++)
				;
			assert(p >= eos || *p < NOTALIT || lglisact(*p));
			p--;
			continue;
		}
		if (lglisact(act))
			*q++ = *c++;
		else
			act = -1;
		d = q;
		satisfied = changed = 0;
#ifndef NDEBUG
		for (p = c; assert(p < eos), (lit = *p); p++) {
			assert(!lglavar(lgl, lit)->mark);
			repr = lglirepr(lgl, lit);
			assert(abs(repr) == 1 || !lglavar(lgl, lit)->mark);
		}
#endif
		lglclnstk(&saved);
		for (p = c; assert(p < eos), (lit = *p); p++) {
			assert(lit < NOTALIT);
			if (druplig)
				lglpushstk(lgl, &saved, lit);
			if (satisfied)
				continue;
			repr = lglirepr(lgl, lit);
			val = lglcval(lgl, repr);
			if (val > 0) {
				satisfied = 1;
				continue;
			}
			if (val < 0) {
				changed = 1;
				continue;
			}
			mark = lglmarked(lgl, repr);
			if (mark < 0) {
				satisfied = 1;
				continue;
			}
			if (mark > 0) {
				changed = 1;
				continue;
			}
			if (lit != repr)
				changed = 1;
			lglmark(lgl, repr);
			*q++ = repr;
		}
		oldsz = p - c;
		for (r = d; r < q; r++)
			lglunmark(lgl, *r);
		newsz = q - d;
		assert(satisfied || newsz == oldsz || changed);
		if (druplig && !satisfied && newsz > 1 && changed)
			*q = 0, lgldrupligaddclsaux(lgl, REDCS, d);
		if (satisfied || !oldsz) {
			q = d - (act >= 0);
			continue;
		}
		if (newsz >= 4) {
			assert(act < 0 || d[-1] == act);
			*q++ = 0;
			assert(d <= c);
		} else if (!newsz) {
			LOG(1, "found empty clause while cleaning decomposition");
			lglmt(lgl);
			q = d - (act >= 0);
		} else if (newsz == 1) {
			LOG(1, "new unit %d while cleaning decomposition", d[0]);
			lglunit(lgl, d[0]);
			q = d - (act >= 0);
		} else if (newsz == 2) {
			t = red ? &lgl->dis->red.bin : &lgl->dis->irr.bin;
			if (s != t) {
				lglpushstk(lgl, t, d[0]);
				lglpushstk(lgl, t, d[1]);
				lglpushstk(lgl, t, 0);
				q = d - (act >= 0);
			} else
				*q++ = 0;
		} else {
			assert(newsz == 3);
			t = red ? &lgl->dis->red.trn : &lgl->dis->irr.trn;
			if (s != t) {
				lglpushstk(lgl, t, d[0]);
				lglpushstk(lgl, t, d[1]);
				lglpushstk(lgl, t, d[2]);
				lglpushstk(lgl, t, 0);
				q = d - (act >= 0);
			} else
				*q++ = 0;
		}
		if (druplig && (satisfied || changed)) {
			lglpushstk(lgl, &saved, 0);
			lgldrupligdelclsaux(lgl, saved.start);
		}
	}
	s->top = q;
	lglrelstk(lgl, &saved);
}

static void
lgldcpconnaux(LGL* lgl, int red, int glue, Stk* s)
{
	int *start = s->start, *q, *d, lit, size, lidx, act;
	const int *p, *c, *eos = s->top;
	assert(red == 0 || red == REDCS);
	assert(!glue || red);
	q = start;
	for (c = q; c < eos; c = p + 1) {
		if (lglisact(act = *c))
			*q++ = *c++;
		else
			act = -1;
		d = q;
		for (p = c; (lit = *p); p++) {
			assert(!lgl->repr[abs(lit)]);
			assert(!lgl->vals[abs(lit)]);
			*q++ = lit;
		}
		size = q - d;
		if (size == 2) {
			q = d - (act >= 0);
			lglwchbin(lgl, d[0], d[1], red);
			lglwchbin(lgl, d[1], d[0], red);
		} else if (size == 3) {
			q = d - (act >= 0);
			lglwchtrn(lgl, d[0], d[1], d[2], red);
			lglwchtrn(lgl, d[1], d[0], d[2], red);
			lglwchtrn(lgl, d[2], d[0], d[1], red);
		} else {
			assert(size > 3);
			*q++ = 0;
			lidx = d - start;
			if (red) {
				assert(lidx <= MAXREDLIDX);
				lidx <<= GLUESHFT;
				assert(0 <= lidx);
				lidx |= glue;
			}
			(void)lglwchlrg(lgl, d[0], d[1], red, lidx);
			(void)lglwchlrg(lgl, d[1], d[0], red, lidx);
		}
	}
	s->top = q;
}

static void
lgldcpcon(LGL* lgl)
{
	Stk* lir;
	int glue;
	lgldcpconnaux(lgl, 0, 0, &lgl->dis->irr.bin);
	lgldcpconnaux(lgl, REDCS, 0, &lgl->dis->red.bin);
	lgldcpconnaux(lgl, 0, 0, &lgl->dis->irr.trn);
	lgldcpconnaux(lgl, REDCS, 0, &lgl->dis->red.trn);
	lglrelstk(lgl, &lgl->dis->irr.bin);
	lglrelstk(lgl, &lgl->dis->irr.trn);
	lglrelstk(lgl, &lgl->dis->red.bin);
	lglrelstk(lgl, &lgl->dis->red.trn);
	DEL(lgl->dis, 1);
	lgldcpconnaux(lgl, 0, 0, &lgl->irr);
	for (glue = 0; glue < MAXGLUE; glue++) {
		lir = lgl->red + glue;
		lgldcpconnaux(lgl, REDCS, glue, lir);
	}
	lglfullyconnected(lgl);
}

static void
lgldcpcln(LGL* lgl)
{
	int glue, old, rounds = 0;
	Stk* lir;
	do {
		rounds++;
		old = lgl->stats->fixed.current;
		lgldcpclnstk(lgl, 0, &lgl->irr);
		lgldcpclnstk(lgl, 0, &lgl->dis->irr.bin);
		lgldcpclnstk(lgl, 0, &lgl->dis->irr.trn);
		lgldcpclnstk(lgl, REDCS, &lgl->dis->red.bin);
		lgldcpclnstk(lgl, REDCS, &lgl->dis->red.trn);
		for (glue = 0; glue < MAXGLUE; glue++) {
			lir = lgl->red + glue;
			lgldcpclnstk(lgl, REDCS, lir);
		}
	} while (old < lgl->stats->fixed.current);
	LOG(1, "iterated %d decomposition cleaning rounds", rounds);
}

static void
lglepush(LGL* lgl, int ilit)
{
	int elit = ilit ? lglexport(lgl, ilit) : 0;
	lglpushstk(lgl, &lgl->extend, elit);
	LOG(4, "pushing external %d internal %d", elit, ilit);
}

static void
lglemerge(LGL* lgl, int ilit0, int ilit1)
{
	int elit0 = lglexport(lgl, ilit0), elit1 = lglexport(lgl, ilit1);
	int repr0 = lglerepr(lgl, elit0), repr1 = lglerepr(lgl, elit1);
	Ext* ext0 = lglelit2ext(lgl, repr0);
	Ext* ext1 = lglelit2ext(lgl, repr1);
#ifndef NDEBUG
	int repr = repr1;
#endif
	assert(abs(repr0) != abs(repr1));
	if (repr0 < 0) {
#ifndef NLGLOG
		repr0 *= -1;
#endif
		repr1 *= -1;
	}
	ext0->equiv = 1;
	ext0->repr = repr1;
	if (ext1->aliased)
		ext0->aliased = 1;
	LOG(2, "merging external literals %d and %d", repr0, repr1);
	assert(lglerepr(lgl, elit0) == repr);
	assert(lglerepr(lgl, elit1) == repr);
	assert(!(ext0->frozen || ext0->tmpfrozen) || ext1->frozen || ext1->tmpfrozen);
	lglepush(lgl, -ilit0);
	lglepush(lgl, ilit1);
	lglepush(lgl, 0);
	lglepush(lgl, ilit0);
	lglepush(lgl, -ilit1);
	lglepush(lgl, 0);
	lgldrupligaddclsarg(lgl, REDCS, -ilit0, ilit1, 0);
	lgldrupligaddclsarg(lgl, REDCS, ilit0, -ilit1, 0);
}

static void
lglimerge(LGL* lgl, int lit, int repr)
{
	int idx = abs(lit);
	AVar* av = lglavar(lgl, idx);
	assert(!lglifrozen(lgl, lit) || lglifrozen(lgl, repr));
	if (lit < 0)
		repr = -repr;
	assert(av->type == FREEVAR);
	assert(lgl->repr);
	av->type = EQUIVAR;
	lgl->repr[idx] = repr;
	lgl->stats->prgss++;
	lgl->stats->irrprgss++;
	lgl->stats->equiv.sum++;
	lgl->stats->equiv.current++;
	assert(lgl->stats->equiv.sum > 0);
	assert(lgl->stats->equiv.current > 0);
	lglemerge(lgl, idx, repr);
}

static void
lglfreezer(LGL* lgl)
{
	int frozen, melted, tmpfrozen, elit, erepr, ilit;
	Ext *ext, *rext;
	int *p, eass;
	if (lgl->frozen)
		return;
	for (elit = 1; elit <= lgl->maxext; elit++)
		lgl->ext[elit].tmpfrozen = 0;
	tmpfrozen = frozen = 0;
	if (!lglmtstk(&lgl->eassume)) {
		for (p = lgl->eassume.start; p < lgl->eassume.top; p++) {
			eass = *p;
			ext = lglelit2ext(lgl, eass);
			assert(!ext->melted);
			assert(!ext->eliminated);
			assert(!ext->blocking);
			if (!ext->frozen && !ext->tmpfrozen) {
				ext->tmpfrozen = 1;
				tmpfrozen++;
				LOG(2, "temporarily freezing external assumption %d", eass);
				erepr = lglerepr(lgl, eass);
				rext = lglelit2ext(lgl, erepr);
				if (ext != rext && !rext->frozen && !rext->tmpfrozen) {
					assert(!rext->equiv);
					assert(!rext->eliminated);
					// assert (!rext->blocking);
					LOG(2, "temporarily freezing external assumption literal %d", erepr);
					rext->tmpfrozen = 1;
					tmpfrozen++;
				}
			}
		}
	}
	for (elit = 1; elit <= lgl->maxext; elit++) {
		ext = lglelit2ext(lgl, elit);
		if (!ext->frozen)
			continue;
		frozen++;
		assert(!ext->melted);
		assert(!ext->eliminated);
		assert(!ext->blocking);
		erepr = lglerepr(lgl, elit);
		rext = lglelit2ext(lgl, erepr);
		if (ext == rext)
			continue;
		if (rext->frozen)
			continue;
		if (rext->tmpfrozen)
			continue;
		// assert (!rext->melted);
		assert(!rext->equiv);
		assert(!rext->eliminated);
		// assert (!rext->blocking);
		LOG(2, "temporarily freezing external literal %d", erepr);
		rext->tmpfrozen = 1;
		tmpfrozen++;
	}
	melted = 0;
	for (elit = 1; elit <= lgl->maxext; elit++) {
		ext = lglelit2ext(lgl, elit);
		if (ext->frozen)
			continue;
		if (ext->melted)
			continue;
		if (ext->tmpfrozen)
			continue;
		if (!ext->imported)
			continue;
		LOG(2, "permanently melted external %d", elit);
		ext->melted = 1;
		melted++;
	}
	LOG(2, "found %d frozen external variables", frozen);
	LOG(2, "temporarily frozen %d external variables", tmpfrozen);
	LOG(2, "permanently melted %d external variables", melted);
	lgl->frozen = 1;
	LOG(2, "frozen solver");
	melted = frozen = 0;
	for (ilit = 2; ilit < lgl->nvars; ilit++) {
		if (!lglisfree(lgl, ilit))
			continue;
		if (lglifrozen(lgl, ilit))
			frozen++;
		else
			melted++;
	}
	lgl->allfrozen = !melted;
	if (lgl->allfrozen)
		lglprt(lgl, 1, "[freezer] all %d free variables frozen", frozen);
	else
		lglprt(lgl,
			   1,
			   "[freezer] frozen %d variables out of %d free variables %.0f%%",
			   frozen,
			   frozen + melted,
			   lglpcnt(frozen, frozen + melted));
}

static int
lglcmprepr(LGL* lgl, int a, int b)
{
	int f = lglifrozen(lgl, a), g = lglifrozen(lgl, b), res;
	if ((res = g - f))
		return res;
	if ((res = (abs(a) - abs(b))))
		return res;
	return a - b;
}

static int
lgltarjan(LGL* lgl)
{
	int *dfsimap, *mindfsimap, idx, oidx, sign, lit, blit, tag, other;
	int dfsi, mindfsi, ulit, uother, tmp, repr, res, sgn, frozen;
	const int *p, *w, *eow;
	Stk stk, component;
	AVar* av;
	HTS* hts;
	if (lgl->mt)
		return 0;
	if (!lgl->nvars)
		return 1;
	assert(lgl->frozen);
	dfsi = 0;
	NEW(dfsimap, 2 * lgl->nvars);
	NEW(mindfsimap, 2 * lgl->nvars);
	NEW(lgl->repr, lgl->nvars);
	CLR(stk);
	CLR(component);
	res = 1;
	for (idx = 2; idx < lgl->nvars; idx++) {
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			ulit = lglulit(lit);
			tmp = dfsimap[ulit];
			if (tmp)
				continue;
			lglpushstk(lgl, &stk, lit);
			while (!lglmtstk(&stk)) {
				lit = lglpopstk(&stk);
				if (lit) {
					ulit = lglulit(lit);
					if (dfsimap[ulit])
						continue;
					dfsimap[ulit] = mindfsimap[ulit] = ++dfsi;
					lglpushstk(lgl, &component, lit);
					assert(dfsi > 0);
					lglpushstk(lgl, &stk, lit);
					lglpushstk(lgl, &stk, 0);
					hts = lglhts(lgl, -lit);
					if (!hts->offset)
						continue;
					assert(hts->count > 0);
					w = lglhts2wchs(lgl, hts);
					eow = w + hts->count;
					for (p = w; p < eow; p++) {
						blit = *p;
						tag = blit & MASKCS;
						if (tag != BINCS) {
							p++;
							continue;
						}
						other = blit >> RMSHFT;
						uother = lglulit(other);
						tmp = dfsimap[uother];
						if (tmp)
							continue;
						lglpushstk(lgl, &stk, other);
					}
				} else {
					assert(!lglmtstk(&stk));
					lit = lglpopstk(&stk);
					ulit = lglulit(lit);
					mindfsi = dfsimap[ulit];
					assert(mindfsi);
					hts = lglhts(lgl, -lit);
					w = lglhts2wchs(lgl, hts);
					eow = w + hts->count;
					for (p = w; p < eow; p++) {
						blit = *p;
						tag = blit & MASKCS;
						if (tag != BINCS) {
							p++;
							continue;
						}
						other = blit >> RMSHFT;
						uother = lglulit(other);
						tmp = mindfsimap[uother];
						if (tmp >= mindfsi)
							continue;
						mindfsi = tmp;
					}
					if (mindfsi == dfsimap[ulit]) {
						repr = lit;
						frozen = lglifrozen(lgl, repr);
						for (p = component.top - 1; (other = *p) != lit; p--) {
							if (lglcmprepr(lgl, other, repr) < 0)
								repr = other;
							if (!frozen && lglifrozen(lgl, other))
								frozen = 1;
						}
						while ((other = lglpopstk(&component)) != lit) {
							mindfsimap[lglulit(other)] = INT_MAX;
							if (other == repr)
								continue;
							if (other == -repr) {
								LOG(1, "empty clause since repr[%d] = %d", repr, other);
								lgldrupligaddclsarg(lgl, REDCS, repr, 0);
								lglmt(lgl);
								res = 0;
								goto DONE;
							}
							sgn = lglsgn(other);
							oidx = abs(other);
							tmp = lgl->repr[oidx];
							if (tmp == sgn * repr)
								continue;
							LOG(2, "repr[%d] = %d", oidx, sgn * repr);
							if (tmp) {
								LOG(1,
									"empty clause since repr[%d] = %d and repr[%d] = %d",
									oidx,
									tmp,
									oidx,
									sgn * repr);
								lgldrupligaddclsarg(lgl, REDCS, repr, 0);
								lglmt(lgl);
								res = 0;
								goto DONE;
							} else {
								av = lglavar(lgl, oidx);
								assert(sgn * oidx == other);
								if (av->type == FREEVAR)
									lglimerge(lgl, other, repr);
								else
									assert(av->type == FIXEDVAR);
							}
						}
						mindfsimap[lglulit(lit)] = INT_MAX;
						if (frozen) {
							LOG(2, "equivalence class of %d is frozen", repr);
							assert(lglifrozen(lgl, repr));
						}
					} else
						mindfsimap[ulit] = mindfsi;
				}
			}
		}
	}
DONE:
	lglrelstk(lgl, &stk);
	lglrelstk(lgl, &component);
	DEL(mindfsimap, 2 * lgl->nvars);
	DEL(dfsimap, 2 * lgl->nvars);
	if (!res)
		DEL(lgl->repr, lgl->nvars);
	if (lgl->mt)
		lgldrupligaddclsarg(lgl, REDCS, 0);
	return res;
}

static int
lglsyncunits(LGL* lgl)
{
	int *units, *eou, *p, elit, erepr, ilit, res, count = 0;
	void (*produce)(void*, int);
	int64_t steps;
	Ext* ext;
	Val val;
	if (lgl->mt)
		return 0;
	if (!lgl->cbs)
		return 1;
	if (!lgl->cbs->units.consume.fun)
		return 1;
	assert(!lgl->simp || !lgl->level);
	steps = lglsteps(lgl);
	if (steps < lgl->limits->sync.steps)
		return 1;
	lgl->limits->sync.steps = steps + lgl->opts->syncunint.val;
	lgl->stats->sync.units.consumed.calls++;
	lgl->cbs->units.consume.fun(lgl->cbs->units.consume.state, &units, &eou);
	if (units == eou)
		return 1;
	lgl->stats->sync.units.consumed.tried++;
	produce = lgl->cbs->units.produce.fun;
	lgl->cbs->units.produce.fun = 0;
	for (p = units; !lgl->mt && p < eou; p++) {
		elit = *p;
		erepr = lglerepr(lgl, elit);
		ext = lglelit2ext(lgl, erepr);
		assert(!ext->equiv);
		ilit = ext->repr;
		if (!ilit)
			continue;
		if (erepr < 0)
			ilit = -ilit;
		if (ilit == 1)
			continue;
		if (ilit == -1)
			val = -1;
		else {
			assert(abs(ilit) > 1);
			val = lglval(lgl, ilit);
			if (val && lglevel(lgl, ilit))
				val = 0;
		}
		if (val == 1)
			continue;
		if (val == -1) {
			LOG(1, "mismatching synchronized external unit %d", elit);
			if (lgl->level > 0)
				lglbacktrack(lgl, 0);
			lglmt(lgl);
		} else if (!lglisfree(lgl, ilit))
			continue;
		else {
			assert(!val);
			if (lgl->level > 0)
				lglbacktrack(lgl, 0);
			lglunit(lgl, ilit);
			LOG(2, "importing internal unit %d external %d", lgl->tid, ilit, elit);
			count++;
		}
	}
	lgl->cbs->units.produce.fun = produce;
	if (lgl->cbs->units.consumed.fun)
		lgl->cbs->units.consumed.fun(lgl->cbs->units.consumed.state, count);
	if (count)
		lgl->stats->sync.units.consumed.actual++;
	if (lgl->mt)
		return 0;
	if (count)
		LOG(1, "imported %d units", count);
	if (!count)
		return 1;
	assert(!lgl->level);
	res = lglbcp(lgl);
	if (!res && !lgl->mt)
		lglmt(lgl);
	return res;
}

static int
lglelitblockingoreliminated(LGL* lgl, int elit)
{
	Ext* ext = lglelit2ext(lgl, elit);
	return ext->blocking || ext->eliminated;
}

static int
lglsynclsexist(LGL* lgl)
{
	int len, lit, blit, tag, other, other2;
	int *s, *p, *w, *eow;
	HTS* hts;
	len = lglcntstk(&lgl->clause);
	assert(!len || lgl->clause.top[-1]);
	if (len <= 1)
		return 0;
	s = lgl->clause.start;
	for (p = s + 1; p + 1 < lgl->clause.top; p++) {
		assert(lglmarked(lgl, *p) > 0);
		if (lglhts(lgl, *s)->count > lglhts(lgl, *p)->count)
			SWAP(int, *s, *p);
	}
	hts = lglhts(lgl, (lit = *s));
	assert(lglmarked(lgl, lit) > 0);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == LRGCS)
			continue;
		if (tag == BINCS) {
			other = blit >> RMSHFT;
			if (lglmarked(lgl, other) > 0)
				return 1;
		} else {
			assert(tag == TRNCS);
			if (len <= 2)
				continue;
			other = blit >> RMSHFT;
			if (lglmarked(lgl, other) > 0)
				return 1;
			other2 = *p;
			if (lglmarked(lgl, other2) > 0)
				return 1;
		}
	}
	return 0;
}

static int
lglsyncls(LGL* lgl)
{
	int maxlevel, level, nonfalse, numtrue, res, delta, glue, consumed;
	int *cls, elit, erepr, tmp, ilit, len, newglue;
	const int* p;
	int64_t rate;
	if (lgl->mt)
		return 0;
	if (!lgl->cbs)
		return 1;
	if (!lgl->cbs->cls.consume.fun)
		return 1;
	assert(!lgl->simp);
	if (lgl->stats->confs < lgl->limits->sync.confs)
		return 1;
	delta = lgl->opts->synclsint.val;
	if (lgl->stats->sync.cls.consumed.calls) {
		rate = 100 * lgl->stats->sync.cls.consumed.tried;
		rate /= lgl->stats->sync.cls.consumed.calls;
		LOG(2,
			"syncls tried/calls = %lld/%lld = %d%%",
			(LGLL)lgl->stats->sync.cls.consumed.tried,
			(LGLL)lgl->stats->sync.cls.consumed.calls,
			rate);
		if (rate)
			delta /= rate;
	}
	lgl->limits->sync.confs = lgl->stats->confs + delta;
	lgl->stats->sync.cls.consumed.calls++;
	consumed = 0;
RESTART:
	lgl->cbs->cls.consume.fun(lgl->cbs->cls.consume.state, &cls, &glue);
	if (!cls)
		return 1;
	lgl->stats->sync.cls.consumed.tried++;
	LOGCLS(2, cls, "trying to import external clause");
	assert(lglmtstk(&lgl->clause));
	maxlevel = nonfalse = numtrue = 0;
#ifndef NDEBUG
	for (p = cls; (elit = *p); p++) {
		ilit = lglimport(lgl, elit);
		if (abs(ilit) <= 1)
			continue;
		assert(!lglmarked(lgl, ilit));
	}
#endif
	for (p = cls; (elit = *p); p++) {
		erepr = lglerepr(lgl, elit);
		if (lglelitblockingoreliminated(lgl, erepr))
			break;
		ilit = lglimport(lgl, erepr);
		if (!ilit)
			break;
		if (ilit == 1)
			break;
		if (ilit == -1)
			continue;
		tmp = lglifixed(lgl, ilit);
		if (tmp > 0)
			break;
		if (tmp < 0)
			continue;
		tmp = lglmarked(lgl, ilit);
		if (tmp > 0)
			continue;
		if (tmp < 0)
			break;
		lglpushstk(lgl, &lgl->clause, ilit);
		lglmark(lgl, ilit);
		tmp = lglval(lgl, ilit);
		if (tmp > 0)
			numtrue++;
		else if (!tmp)
			nonfalse++;
		else if ((level = lglevel(lgl, ilit)) > maxlevel)
			maxlevel = level;
	}
	res = 1;
	if (!elit && lglsynclsexist(lgl))
		elit = INT_MAX;
	for (p = lgl->clause.start; p < lgl->clause.top; p++)
		lglunmark(lgl, *p);
	if (elit)
		goto DONE;
	len = lglcntstk(&lgl->clause);
	if (!numtrue && nonfalse <= 1) {
		if (len <= 1 || maxlevel <= 1)
			level = 0;
		else {
			level = 0;
			for (p = lgl->clause.start; p < lgl->clause.top; p++) {
				ilit = *p;
				tmp = lglval(lgl, ilit);
				assert(tmp <= 0);
				if (!tmp)
					continue;
				tmp = lglevel(lgl, ilit);
				if (tmp < maxlevel && tmp > level)
					level = tmp;
			}
		}
		if (level < lgl->level) {
			LOG(2, "importing clause requires to backtrack to level %d", level);
			lglbacktrack(lgl, level);
		}
	}
	lglpushstk(lgl, &lgl->clause, 0);
	newglue = glue;
	LOGCLS(2,
		   lgl->clause.start,
		   "successfully imported as glue %d redundant glue %d length %d clause",
		   newglue,
		   glue,
		   len);
	lgl->stats->sync.cls.consumed.actual++;
	lgldrupligaddcls(lgl, 0); // TODO?
	lgladdcls(lgl, REDCS, newglue, !numtrue);
	consumed++;
	if (lgl->mt)
		res = 0;
	else
		res = lglbcpsearch(lgl);
DONE:
	lglclnstk(&lgl->clause);
	if (res && lgl->opts->synclsall.val)
		goto RESTART;
	if (lgl->cbs->cls.consumed.fun)
		lgl->cbs->cls.consumed.fun(lgl->cbs->cls.consumed.state, consumed);
	return res;
}

static int
lglprbpull(LGL* lgl, int lit, int probe)
{
	AVar* av;
	assert(lgl->level == 1);
	av = lglavar(lgl, lit);
	if (av->mark)
		return 0;
	if (!lglevel(lgl, lit))
		return 0;
	assert(lglevel(lgl, lit) == 1);
	av->mark = 1;
	lglpushstk(lgl, &lgl->seen, -lit);
	LOG(3, "pulled in literal %d during probing analysis", -lit);
	(void)probe;
	return 1;
}

static int
lglprbana(LGL* lgl, int probe)
{
	int open, lit, r0, r1, tag, red, other, res, *p, *rsn;
	assert(lgl->level == 1);
	assert(lgl->conf.lit);
	assert(lglmtstk(&lgl->seen));
	lit = lgl->conf.lit;
	r0 = lgl->conf.rsn[0], r1 = lgl->conf.rsn[1];
	open = lglprbpull(lgl, lit, probe);
	LOG(2, "starting probing analysis with reason of literal %d", lit);
	for (;;) {
		assert(lglevel(lgl, lit) == 1);
		tag = r0 & MASKCS;
		if (tag == BINCS || tag == TRNCS) {
			other = r0 >> RMSHFT;
			if (lglprbpull(lgl, other, probe))
				open++;
			if (tag == TRNCS && lglprbpull(lgl, r1, probe))
				open++;
		} else {
			assert(tag == LRGCS);
			red = r0 & REDCS;
			p = lglidx2lits(lgl, red, r1);
			while ((other = *p++))
				open += lglprbpull(lgl, other, probe);
		}
		LOG(3, "open %d antecedents in probing analysis", open - 1);
		assert(open > 0);
		while (!lglmarked(lgl, lit = lglpopstk(&lgl->trail)))
			lglunassign(lgl, lit);
		lglunassign(lgl, lit);
		if (!--open) {
			res = lit;
			break;
		}
		LOG(2, "analyzing reason of literal %d in probing analysis next", lit);
		rsn = lglrsn(lgl, lit);
		r0 = rsn[0], r1 = rsn[1];
	}
	assert(res);
	if (res == probe)
		LOG(2, "probing analysis returns the probe %d as 1st UIP", probe);
	else
		LOG(2, "probing analysis returns different 1st UIP %d and not probe %d", res, probe);
	lglpopnunmarkstk(lgl, &lgl->seen);
	return res;
}

static int
lglederef(LGL* lgl, int elit)
{
	int ilit, res;
	Ext* ext;
	assert(elit);
	if (abs(elit) > lgl->maxext)
		return -1;
	ext = lglelit2ext(lgl, elit);
	if (!(res = ext->val)) {
		assert(!ext->equiv);
		ilit = ext->repr;
		res = ilit ? lglcval(lgl, ilit) : -1;
	}
	if (elit < 0)
		res = -res;
	return res;
}

static int
lgldecomp(LGL*); // TODO move

static int
lglhasbin(LGL* lgl, int a, int b)
{
	int blit, tag, other, res = 0;
	const int *w, *eow, *p;
	int64_t inc;
	HTS *ha, *hb;
	ha = lglhts(lgl, a);
	hb = lglhts(lgl, b);
	if (hb->count < ha->count) {
		SWAP(int, a, b);
		SWAP(HTS*, ha, hb);
	}
	w = lglhts2wchs(lgl, ha);
	eow = w + ha->count;
	for (p = w; !res && p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == OCCS)
			continue;
		if (tag == TRNCS || tag == LRGCS) {
			p++;
			continue;
		}
		assert(tag == BINCS);
		other = blit >> RMSHFT;
		if (other == b)
			res = 1;
	}
	inc = 2 + (int)(p - w);
	if (lgl->card)
		ADDSTEPS(card.steps, inc);
	if (lgl->simpleprobing)
		ADDSTEPS(prb.simple.steps, inc);
	return res;
}

static void
lglwrkinit(LGL* lgl, int posonly, int fifo)
{
	int size, lit;
	NEW(lgl->wrk, 1);
	lgl->wrk->fifo = fifo;
	size = lgl->wrk->size = lgl->nvars;
	if (posonly) {
		NEW(lgl->wrk->pos, size);
		lgl->wrk->posonly = 1;
	} else {
		NEW(lgl->wrk->pos, 2 * size);
		lgl->wrk->pos += size;
		for (lit = -size + 1; lit < -1; lit++)
			lgl->wrk->pos[lit] = -1;
	}
	for (lit = 2; lit < size; lit++)
		lgl->wrk->pos[lit] = -1;
}

static void
lglwrkreset(LGL* lgl)
{
	lglrelstk(lgl, &lgl->wrk->queue);
	if (lgl->wrk->posonly)
		DEL(lgl->wrk->pos, lgl->wrk->size);
	else {
		lgl->wrk->pos -= lgl->wrk->size;
		DEL(lgl->wrk->pos, 2 * lgl->wrk->size);
	}
	DEL(lgl->wrk, 1);
}

static void
lglwrkcompact(LGL* lgl)
{
	int i, j = 0, lit, tail = lglcntstk(&lgl->wrk->queue);
	for (i = lgl->wrk->head; i < tail; i++) {
		lit = lgl->wrk->queue.start[i];
		if (!lit)
			continue;
		assert(!lgl->wrk->posonly || 1 < lit);
		assert(1 < abs(lit) && abs(lit) < lgl->wrk->size);
		assert(lgl->wrk->pos[lit] == i);
		if (!lglisfree(lgl, lit)) {
			lgl->wrk->pos[lit] = -1;
			assert(lgl->wrk->count > 0);
			lgl->wrk->count--;
		} else {
			lgl->wrk->queue.start[j] = lit;
			lgl->wrk->pos[lit] = j++;
		}
	}
	lglrststk(&lgl->wrk->queue, j);
	lgl->wrk->head = 0;
}

static int
lglwrktouched(LGL* lgl, int lit)
{
	if (lgl->wrk->posonly)
		lit = abs(lit);
	return lgl->wrk->pos[lit] >= 0;
}

static int
lglwrktouch(LGL* lgl, int lit)
{
	int tail, pos;
	if (!lglisfree(lgl, lit))
		return 1;
	if (lgl->donotsched) {
		if (lgl->ternresing && lglavar(lgl, lit)->donoternres)
			return 1;
		if (lgl->simpleprobing && lglavar(lgl, lit)->donotsimpleprobe)
			return 1;
	}
	if (lgl->wrk->posonly)
		lit = abs(lit);
	tail = lglcntstk(&lgl->wrk->queue);
	LOG(4, "work touch %d", lit);
	if ((pos = lgl->wrk->pos[lit]) >= 0) {
		assert(lgl->wrk->queue.start[pos] == lit);
		lgl->wrk->queue.start[pos] = 0;
	}
	lgl->wrk->count++;
	assert(lgl->wrk->count > 0);
	lgl->wrk->pos[lit] = tail;
	lglpushstk(lgl, &lgl->wrk->queue, lit);
	if (tail / 2 > lgl->wrk->count)
		lglwrkcompact(lgl);
	return 1;
}

static int
lglwrkdeq(LGL* lgl)
{
	int res, pos;
	while ((pos = lgl->wrk->head) < (int)lglcntstk(&lgl->wrk->queue)) {
		lgl->wrk->head++;
		res = lgl->wrk->queue.start[pos];
		if (!res)
			continue;
		lgl->wrk->queue.start[pos] = 0;
		assert(lgl->wrk->count > 0);
		lgl->wrk->count--;
		assert(lgl->wrk->pos[res] == pos);
		lgl->wrk->pos[res] = -1;
		if (lglisfree(lgl, res))
			return res;
	}
	return 0;
}

static int
lglwrkpop(LGL* lgl)
{
	int res;
	while ((int)lglcntstk(&lgl->wrk->queue) > lgl->wrk->head) {
		res = lglpopstk(&lgl->wrk->queue);
		if (!res)
			continue;
#ifndef NDEBUG
		{
			int pos = lglcntstk(&lgl->wrk->queue);
			assert(lgl->wrk->pos[res] == pos);
		}
#endif
		lgl->wrk->pos[res] = -1;
		if (lglisfree(lgl, res))
			return res;
	}
	return 0;
}

static int
lglwrknext(LGL* lgl)
{
	return lgl->wrk->fifo ? lglwrkdeq(lgl) : lglwrkpop(lgl);
}

static int
lglrandlitrav(LGL* lgl, int (*fun)(LGL*, int lit))
{
	int delta, mod, prev, first, ulit, count;
	if (lgl->nvars < 2)
		return 0;
	first = mod = 2 * lgl->nvars;
	ulit = lglrand(lgl) % mod;
	delta = lglrand(lgl) % mod;
	if (!delta)
		delta++;
	while (lglgcd(delta, mod) > 1)
		if (++delta == mod)
			delta = 1;
	count = mod;
	LOG(2, "random literal traversal start %d delta %d mod %d", ulit, delta, mod);
	for (;;) {
		count--;
		assert(count >= 0);
		if (ulit >= 4 && !fun(lgl, lglilit(ulit)))
			return 0;
		prev = ulit;
		ulit += delta;
		if (ulit >= mod)
			ulit -= mod;
		if (ulit == first) {
			assert(!count);
			break;
		}
		if (first == mod)
			first = prev;
	}
	return 1;
}

static void
lglsimpleprobeinit(LGL* lgl)
{
	int idx, rem = 0, ret = 0;
	for (idx = 2; idx < lgl->nvars; idx++)
		lgl->avars[idx].equiv = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		if (lglavar(lgl, idx)->donotsimpleprobe)
			ret++;
		else
			rem++;
	}
	if (!rem) {
		ret = 0;
		for (idx = 2; idx < lgl->nvars; idx++) {
			if (!lglisfree(lgl, idx))
				continue;
			lglavar(lgl, idx)->donotsimpleprobe = 0;
			rem++;
		}
	}
	if (!ret)
		lglprt(lgl, 1, "[simpleprobe-%d] all %d free variables schedulable", lgl->stats->prb.simple.count, rem);
	else
		lglprt(lgl,
			   1,
			   "[simpleprobe-%d] %d schedulable variables %.0f%%",
			   lgl->stats->prb.simple.count,
			   rem,
			   lglpcnt(rem, lglrem(lgl)));
	lglwrkinit(lgl, 0, 1);
	assert(!lgl->donotsched), lgl->donotsched = 1;
	lglrandlitrav(lgl, lglwrktouch);
	assert(lgl->donotsched), lgl->donotsched = 0;
	assert(lgl->opts->prbsimple.val);
	lglchkirrstats(lgl);
}

static void
lglsimpleprobereset(LGL* lgl, int nvars)
{
	const int* p;
	int idx;
	lglrelstk(lgl, &lgl->sprb->units);
	lglrelstk(lgl, &lgl->sprb->impls);
	lglrelstk(lgl, &lgl->sprb->eqs);
	for (idx = 2; idx < lgl->nvars; idx++)
		lgl->avars[idx].donotsimpleprobe = 1;
	for (p = lgl->wrk->queue.start; p < lgl->wrk->queue.top; p++)
		lgl->avars[abs(*p)].donotsimpleprobe = 0;
	lglwrkreset(lgl);
	lglrelstk(lgl, &lgl->sprb->counted);
	lglrelstk(lgl, &lgl->sprb->marked);
	assert(nvars == lgl->nvars);
	lgl->sprb->spes -= nvars;
	DEL(lgl->sprb->spes, 2 * nvars);
}

static int
lglflush(LGL*);
static void
lgldense(LGL*, int rmredbintrn);
static void
lglsparse(LGL*);

static int
lglsimpleprobeunits(LGL* lgl)
{
	const int* p;
	int lit, res;
	Val val;
	res = 0;
	lglchkirrstats(lgl);
	for (p = lgl->sprb->units.start; !lgl->mt && p < lgl->sprb->units.top; p++) {
		lit = *p;
		val = lglval(lgl, lit);
		if (val > 0)
			continue;
		lgl->stats->prb.simple.failed++;
		if (val < 0) {
			lgldrupligaddclsarg(lgl, REDCS, lit, 0);
			LOG(1, "inconsistent unit %d", lit);
			lglmt(lgl);
		} else {
			lglunit(lgl, lit);
			res++;
			if (!lglflush(lgl)) {
				LOG(1, "propagating simple HBR unit %d results in conflict", lit);
				lglmt(lgl);
			}
		}
	}
	return res;
}

static int
lglsimpleprobeimpls(LGL* lgl)
{
	int a, b, res;
	res = 0;
	while (!lglmtstk(&lgl->sprb->impls)) {
		b = lglpopstk(&lgl->sprb->impls);
		a = lglpopstk(&lgl->sprb->impls);
		if (lglval(lgl, a) || lglval(lgl, b))
			continue;
		if (lglhasbin(lgl, a, b))
			continue;
		LOG(2, "adding previously detected hyper binary resolvent %d %d", a, b);
		lgldrupligaddclsarg(lgl, REDCS, a, b, 0);
		res++;
		lgl->stats->hbr.cnt++;
		lgl->stats->hbr.simple++;
		(void)lglwchbin(lgl, a, b, REDCS);
		(void)lglwchbin(lgl, b, a, REDCS);
		lgl->stats->red.bin++, assert(lgl->stats->red.bin > 0);
		lglwrktouch(lgl, -a);
		lglwrktouch(lgl, -b);
		if (!lglhasbin(lgl, -a, -b))
			continue;
		LOG(2, "found equivalent literals %d and %d", -a, b);
		lglpushstk(lgl, &lgl->sprb->eqs, -a);
		lglpushstk(lgl, &lgl->sprb->eqs, b);
	}
	return res;
}

static int
lglhasonlybin(LGL* lgl, int lit, int other)
{
	HTS* hts = lglhts(lgl, lit);
	const int* w;
	int blit;
	if (hts->count != 1)
		return 0;
	w = lglhts2wchs(lgl, hts);
	blit = *w;
	if ((blit & MASKCS) != BINCS)
		return 0;
	return (blit >> RMSHFT) == other;
}

static int
lglsimpleprobemerged(LGL* lgl, int a, int b)
{
	return lglhasonlybin(lgl, -a, b) && lglhasonlybin(lgl, -b, a);
}

static int
lglflushclauses(LGL* lgl, int lit);

static void
lglsimpleprobeclscp(LGL* lgl, int nonrepr, int repr, Stk* stk)
{
	int blit, tag, red, other, other2, lidx, count, found, start, trivial;
	const int *p, *w, *eow, *c, *q;
	HTS* hts = lglhts(lgl, nonrepr);
	assert(abs(repr) < abs(nonrepr));
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	count = 0;
	for (p = w; p < eow; p++) {
		INCSTEPS(prb.simple.steps);
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == LRGCS)
			continue;
		start = lglcntstk(stk);
		trivial = 0;
		red = blit & REDCS;
		lglpushstk(lgl, stk, red);
		lglpushstk(lgl, stk, repr);
		if (tag == BINCS || tag == TRNCS) {
			other = blit >> RMSHFT;
			if (other == -repr)
				trivial = 1;
			else if (other != repr)
				lglpushstk(lgl, stk, other);
			if (tag == TRNCS) {
				other2 = *p;
				if (other2 == -repr)
					trivial = 1;
				else if (other2 != repr)
					lglpushstk(lgl, stk, other2);
			}
		} else {
			assert(tag == OCCS);
			lidx = blit >> RMSHFT;
			c = lglidx2lits(lgl, red, lidx);
			found = 0;
			for (q = c; !trivial && (other = *q); q++) {
				if (other >= NOTALIT)
					trivial = 1;
				else if (other == nonrepr)
					found++;
				else if (other == -repr)
					trivial = 1;
				else if (other != repr)
					lglpushstk(lgl, stk, other);
			}
			assert(trivial || found);
		}
		if (trivial)
			lglrststk(stk, start);
		else
			lglpushstk(lgl, stk, 0), count++;
	}
	LOG(2, "copied %d clauses with %d replacing %d by %d", count, nonrepr, nonrepr, repr);
}

static int
lglsimpleprobebinexists(LGL* lgl, int a, int b)
{
	const int *p, *w, *eow;
	int blit, tag, red, other;
	HTS* hts;
	hts = lglhts(lgl, a);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag != BINCS)
			continue;
		red = blit & REDCS;
		if (red)
			continue;
		other = blit >> RMSHFT;
		if (other == b)
			return 1;
	}
	return 0;
}

static int
lglsimpleprobetrnexists(LGL* lgl, int a, int b, int c)
{
	int blit, tag, red, other, other2;
	const int *p, *w, *eow;
	HTS* hts;
	hts = lglhts(lgl, a);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		red = blit & REDCS;
		if (red)
			continue;
		other = blit >> RMSHFT;
		if (tag == BINCS) {
			if (other == b)
				return 1;
			if (other == c)
				return 1;
		} else if (tag == TRNCS) {
			other2 = *p;
			if (other == b && other2 == c)
				return 1;
			if (other == c && other2 == b)
				return 1;
		}
	}
	return 0;
}

static int
lglsimpleprobelrgexists(LGL* lgl, int a)
{
	int blit, tag, red, other, other2, lidx, res;
	const int *p, *w, *eow, *c, *q;
	HTS* hts;
	for (p = lgl->clause.start; p + 1 < lgl->clause.top; p++) {
		other = *p;
		assert(!lglsignedmarked(lgl, other));
		lglsignedmark(lgl, other);
	}
	assert(lglsignedmarked(lgl, a));
	hts = lglhts(lgl, a);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	res = 0;
	for (p = w; !res && p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		red = blit & REDCS;
		if (red || tag == LRGCS)
			continue;
		other = blit >> RMSHFT;
		if (tag == BINCS) {
			res = lglsignedmarked(lgl, other);
		} else if (tag == TRNCS) {
			other2 = *p;
			res = lglsignedmarked(lgl, other) && lglsignedmarked(lgl, other2);
		} else {
			assert(tag == OCCS);
			lidx = other;
			c = lglidx2lits(lgl, 0, lidx);
			for (q = c; (other = *q); q++)
				if (!lglsignedmarked(lgl, other))
					break;
			res = !other;
		}
	}
	for (p = lgl->clause.start; p + 1 < lgl->clause.top; p++)
		lglunmark(lgl, *p);
	return res;
}

static int
lglsimpleprobeclausexists(LGL* lgl)
{
	int len = lglcntstk(&lgl->clause) - 1, a, b, c, *p, *s, res;
	assert(len >= 0);
	assert(!lgl->clause.top[-1]);
	s = lgl->clause.start;
	for (p = s + 1; p + 1 < lgl->clause.top; p++)
		if (lglhts(lgl, *s)->count > lglhts(lgl, *p)->count)
			SWAP(int, *s, *p);
	a = lgl->clause.start[0];
	if (len == 2) {
		b = lgl->clause.start[1];
		res = lglsimpleprobebinexists(lgl, a, b);
	} else if (len == 3) {
		b = lgl->clause.start[1];
		c = lgl->clause.start[2];
		res = lglsimpleprobetrnexists(lgl, a, b, c);
	} else if (len > 3)
		res = lglsimpleprobelrgexists(lgl, a);
	else
		res = 0;
	if (res)
		LOG(2, "will not add already existing clause");
	return res;
}

static void
lglsimpleprobeaddclausesonstack(LGL* lgl, Stk* stk)
{
	int count, red, lit, size;
	const int *c, *p, *q;
	count = 0;
	for (c = stk->start; c < stk->top; c = p + 1) {
		red = *(p = c);
		if (p[1] == REMOVED) {
			p++;
			while (p + 1 < stk->top && p[1] == REMOVED)
				p++;
			continue;
		}
		assert(!red || red == REDCS);
		assert(lglmtstk(&lgl->clause));
		do {
			lit = *++p;
			lglpushstk(lgl, &lgl->clause, lit);
		} while (lit);
		if (!lglisimpcls(lgl) && !lglsimpleprobeclausexists(lgl)) {
			lgldrupligaddcls(lgl, REDCS);
			q = lgl->clause.start;
			size = lglcntstk(&lgl->clause);
			assert(size > 0);
			assert(!q[size - 1]);
			size--;
			if (size > 2 && lgl->opts->prbsimple.val >= 3)
				while ((lit = *q++))
					lglwrktouch(lgl, lit);
			if (size == 2 && lgl->opts->prbsimple.val >= 2)
				lglwrktouch(lgl, -q[0]), lglwrktouch(lgl, -q[1]);
			lgladdcls(lgl, red, 0, 1);
			count++;
		}
		lglclnstk(&lgl->clause);
		if (!lglflush(lgl))
			break;
	}
	LOG(2, "added %d non-trivial clauses", count);
}

static void
lglsimpleprobeaddprbincls(LGL* lgl, int a, int b)
{
	assert(lgl->probing);
	assert(lglmtstk(&lgl->clause));
	assert(abs(a) != abs(b));
	assert(!lglval(lgl, a));
	assert(!lglval(lgl, b));
	lglpushstk(lgl, &lgl->clause, a);
	lglpushstk(lgl, &lgl->clause, b);
	lglpushstk(lgl, &lgl->clause, 0);
	if (!lglsimpleprobeclausexists(lgl)) {
		LOG(2, "added simple probing connecting binary clause", a, b);
		lgldrupligaddcls(lgl, REDCS);
		lgladdcls(lgl, 0, 0, 1);
	}
	lglclnstk(&lgl->clause);
}

static int
lglcmpilit(int* a, int* b)
{
	int res, l = *a, k = *b;
	if ((res = abs(l) - abs(k)))
		return res;
	return l - k;
}

static int
lglcmpcls(LGL* lgl, const int* c, const int* d)
{
	const int *p, *q;
	if (*c >= REMOVED && *d >= REMOVED)
		return c - d;
	if (*c >= REMOVED)
		return 1;
	if (*d >= REMOVED)
		return -1;
	for (p = c, q = d; *p && *q == *p; p++, q++)
		;
	(void)lgl;
	return *p - *q;
}

static int
lglcmpsz(LGL* lgl, const int* start, PSz* p, PSz* q)
{
	const int *c, *d;
	int res;
	if ((res = p->size - q->size))
		return res;
	c = start + p->pos, d = start + q->pos;
	if ((res = lglcmpcls(lgl, c, d)))
		return res;
	if ((res = c[-1] - d[-1]))
		return res;
	(void)lgl;
	return p->pos - q->pos;
}

#define LGLCMPSZ(A, B) (lglcmpsz(lgl, start, (A), (B)))

static void
lglrmdupclsonstack(LGL* lgl, Stk* stk, Stk* sort)
{
	int *start, *c, *p, *d, pos, size, i;
	PSz* ps;
	lglclnstk(sort);
	start = stk->start;
	for (c = start; c < stk->top; c = p + 1) {
		c++;
		assert(c < stk->top);
		pos = c - start;
		size = 0;
		for (p = c; *p; p++)
			size++;
		SORT(int, c, size, lglcmpilit);
		lglpushstk(lgl, sort, pos);
		lglpushstk(lgl, sort, size);
	}
	size = lglcntstk(sort) / 2;
	if (size <= 1)
		return;
	ps = (PSz*)sort->start;
	SORT(PSz, ps, size, LGLCMPSZ);
	for (i = 1; i < size; i++) {
		if (ps[i - 1].size != ps[i].size)
			continue;
		c = stk->start + ps[i - 1].pos;
		d = stk->start + ps[i].pos;
		if (lglcmpcls(lgl, c, d))
			continue;
		LOGCLS(4, d, "not copying duplicate %s clause", lglred2str(d[-1]));
		while (*d)
			*d++ = REMOVED;
		*d = REMOVED;
	}
}

static void
lglsimpleprobemerge(LGL* lgl, int repr, int nonrepr)
{
	const int druplig = lgl->opts->druplig.val;
	Val valrepr, valnonrepr;
	Stk stk, sort;
	int unit;
	lglchkirrstats(lgl);
	lgl->stats->prb.simple.eqs++;
	assert(abs(repr) < abs(nonrepr));
	lglavar(lgl, nonrepr)->equiv = 1;
	CLR(stk);
	CLR(sort);
	if (druplig) {
		lgldrupligaddclsarg(lgl, REDCS, repr, -nonrepr, 0), lgldrupligaddclsarg(lgl, REDCS, -repr, nonrepr, 0);
	}
	lglsimpleprobeclscp(lgl, nonrepr, repr, &stk);
	lglsimpleprobeclscp(lgl, -nonrepr, -repr, &stk);
	lglrmdupclsonstack(lgl, &stk, &sort);
	lglsimpleprobeaddclausesonstack(lgl, &stk);
	lglrelstk(lgl, &sort);
	lglrelstk(lgl, &stk);
	lglflushclauses(lgl, nonrepr);
	lglflushclauses(lgl, -nonrepr);
	if (lgl->mt)
		goto FIXDRUPLIG;
	valrepr = lglval(lgl, repr);
	valnonrepr = lglval(lgl, nonrepr);
	if (valrepr && valnonrepr == valrepr)
		goto FIXDRUPLIG;
	if (valrepr && valrepr == -valrepr) {
		LOG(1, "equality between %d and %d became inconsistent", repr, nonrepr);
		lglmt(lgl);
		goto FIXDRUPLIG;
	}
	if (valrepr && !valnonrepr)
		unit = valrepr < 0 ? -nonrepr : nonrepr;
	else if (!valrepr && valnonrepr)
		unit = valnonrepr < 0 ? -repr : repr;
	else
		unit = 0, assert(!valrepr), assert(!valnonrepr);
	if (unit) {
		LOG(1, "new unit %d from equality between %d and %d", unit, repr, nonrepr);
		lglunit(lgl, unit);
		(void)lglflush(lgl);
	} else
		lglsimpleprobeaddprbincls(lgl, repr, -nonrepr), lglsimpleprobeaddprbincls(lgl, -repr, nonrepr);
	lglchkirrstats(lgl);
FIXDRUPLIG:
	if (druplig) {
		lgldrupligdelclsarg(lgl, repr, -nonrepr, 0), lgldrupligdelclsarg(lgl, -repr, nonrepr, 0);
	}
}

static int
lglsimpleprobeqs(LGL* lgl)
{
	int a, b, res;
	res = 0;
	while (!lgl->mt && !lglmtstk(&lgl->sprb->eqs)) {
		a = lglpopstk(&lgl->sprb->eqs);
		b = lglpopstk(&lgl->sprb->eqs);
		if (lglval(lgl, a))
			continue;
		assert(!lglval(lgl, b));
		assert(abs(a) != abs(b));
		if (lglsimpleprobemerged(lgl, a, b))
			continue;
		if (abs(b) < abs(a))
			SWAP(int, a, b);
		lglsimpleprobemerge(lgl, a, b);
		res++;
	}
	return res;
}

static SPE*
lglspe(LGL* lgl, int lit)
{
	assert(2 <= abs(lit) && abs(lit) < lgl->nvars);
	return lgl->sprb->spes + lit;
}

static void
lglsimpleprobehbr(LGL* lgl, int touched, const int* c)
{
	int blit, tag, lit, other, count, val, maxcount, rem, first, second;
	const int *p, *q, *w, *eow;
	unsigned sum, tmp;
	SPE* spe;
	HTS* hts;
	first = second = count = sum = 0;
	for (p = c; (lit = *p); p++) {
		if (lglwrktouched(lgl, lit))
			return;
		if ((val = lglval(lgl, lit)) > 0)
			return;
		if (val < 0)
			continue;
		if (!first)
			first = lit;
		else if (!second)
			second = lit;
		sum += (unsigned)lit;
		count++;
	}
	if (count == 2) {
		assert(first && second);
		if (lglhasbin(lgl, -first, -second)) {
			LOG(2, "found equivalent literals %d and %d", -first, second);
			lglpushstk(lgl, &lgl->sprb->eqs, -first);
			lglpushstk(lgl, &lgl->sprb->eqs, second);
			goto DONE;
		}
	}
	rem = count;
	maxcount = 0;
	assert(lglmtstk(&lgl->sprb->counted));
	for (p = c; (lit = *p); p++) {
		if (INCSTEPS(prb.simple.steps) >= lgl->limits->prb.steps)
			goto DONE;
		val = lglval(lgl, lit);
		assert(val <= 0);
		if (val)
			continue;
		hts = lglhts(lgl, -lit);
		w = lglhts2wchs(lgl, hts);
		eow = w + hts->count;
		assert(lglmtstk(&lgl->sprb->marked));
		for (q = w; q < eow; q++) {
			if (INCSTEPS(prb.simple.steps) >= lgl->limits->prb.steps)
				goto DONE;
			blit = *q;
			tag = blit & MASKCS;
			if (tag == TRNCS || tag == LRGCS)
				q++;
			if (tag != BINCS)
				continue;
			other = blit >> RMSHFT;
			val = lglval(lgl, other);
			assert(val >= 0);
			if (val)
				continue;
			if ((spe = lglspe(lgl, other))->mark)
				continue;
			if (lglspe(lgl, -other)->mark) {
				LOG(2, "unit %d through very simple hyper binary resolution", -lit);
				lglpushstk(lgl, &lgl->sprb->units, -lit);
				goto DONE;
			}
			if (!spe->count)
				lglpushstk(lgl, &lgl->sprb->counted, other);
			spe->sum += (unsigned)lit;
			spe->count++;
			spe->mark = 1;
			lglpushstk(lgl, &lgl->sprb->marked, other);
			if (spe->count > maxcount)
				maxcount = spe->count;
		}
		while (!lglmtstk(&lgl->sprb->marked)) {
			other = lglpopstk(&lgl->sprb->marked);
			spe = lglspe(lgl, other);
			assert(spe->mark);
			spe->mark = 0;
		}
		rem--;
		assert(maxcount + rem <= count);
		if (maxcount + rem + 1 < count)
			goto DONE;
	}
	if (count <= 2)
		goto DONE;
	for (q = lgl->sprb->counted.start; q < lgl->sprb->counted.top; q++) {
		other = *q;
		assert(!lglval(lgl, other));
		spe = lglspe(lgl, other);
		assert(spe->count <= count);
		if (spe->count == count) {
			LOG(2, "unit %d through simple hyper binary resolution", other);
			lglpushstk(lgl, &lgl->sprb->units, other);
		} else if (spe->count + 1 == count) {
			tmp = sum - spe->sum;
			lit = (int)tmp;
			assert(2 <= abs(lit) && abs(lit) < lgl->nvars);
			if (abs(lit) != abs(other) && !lglhasbin(lgl, other, lit)) {
				LOG(2, "simple hyper binary resolvent %d %d", other, lit);
				lglpushstk(lgl, &lgl->sprb->impls, other);
				lglpushstk(lgl, &lgl->sprb->impls, lit);
			}
		}
	}
	for (p = c; (lit = *p); p++) {
		if (lglval(lgl, lit))
			continue;
		spe = lglspe(lgl, lit);
		if (spe->count + 2 != count)
			continue;
		tmp = sum - spe->sum;
		tmp -= (unsigned)lit;
		other = (int)tmp;
		assert(2 <= abs(other) && abs(other) < lgl->nvars);
		if (abs(lit) != abs(other) && !lglhasbin(lgl, other, lit)) {
			LOG(2, "self-subsuming hyper binary resolvent %d %d", other, lit);
			lglpushstk(lgl, &lgl->sprb->impls, other);
			lglpushstk(lgl, &lgl->sprb->impls, lit);
		}
	}
DONE:
	while (!lglmtstk(&lgl->sprb->counted)) {
		lit = lglpopstk(&lgl->sprb->counted);
		spe = lglspe(lgl, lit);
		CLRPTR(spe);
	}
	lglclnstk(&lgl->sprb->marked);
	(void)touched;
}

static void
lglpushnmarkseen(LGL* lgl, int lit)
{
	lglpushstk(lgl, &lgl->seen, lit);
	lglmarkunmarked(lgl, lit);
}

static void
lglsignedmarknpushseen(LGL* lgl, int lit)
{
	lglsignedmark(lgl, lit);
	lglpushstk(lgl, &lgl->seen, lit);
}

static void
lglsimpleliftmark(LGL* lgl, int root, int level)
{
	const int *p, *w, *eow;
	int blit, tag, other;
	HTS* hts;
	INCSTEPS(prb.simple.steps);
	assert(level > 0);
	assert(lglsignedmarked(lgl, -root));
	LOG(3, "next simple lifting marking from %d at level %d", root, level);
	hts = lglhts(lgl, root);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag != BINCS)
			continue;
		other = blit >> RMSHFT;
		if (lglsignedmarked(lgl, other))
			continue;
		lglsignedmarknpushseen(lgl, other);
		LOG(4, "next simple lifting marking from %d at level %d marked %d", root, level, other);
		if (level > 1)
			lglsimpleliftmark(lgl, -other, level - 1);
	}
}

static void
lglsimpleliftcollect(LGL* lgl, int root, int start, int level)
{
	const int *p, *w, *eow;
	int blit, tag, other;
	HTS* hts;
	INCSTEPS(prb.simple.steps);
	assert(level > 0);
	LOG(3, "next simple lifting collecting from %d at level %d", start, level);
	hts = lglhts(lgl, start);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag != BINCS)
			continue;
		other = blit >> RMSHFT;
		if (other == -root)
			continue;
		if (lglsignedmarked(lgl, other)) {
			LOG(2, "simple lifted unit %d with root %d", other, root);
			lglpushstk(lgl, &lgl->sprb->units, other);
		} else if (other != root && lglsignedmarked(lgl, -other)) {
			LOG(2, "found equivalent literals %d and %d", root, other);
			lglpushstk(lgl, &lgl->sprb->eqs, root);
			lglpushstk(lgl, &lgl->sprb->eqs, other);
		}
		if (level > 1)
			lglsimpleliftcollect(lgl, root, -other, level - 1);
	}
}

static void
lglsimplelift(LGL* lgl, int root)
{
	int count, pos, neg;
	pos = lglhts(lgl, root)->count;
	neg = lglhts(lgl, -root)->count;
	if (neg < pos)
		root = -root;
	LOG(2, "next simple lifting %d", root);
	assert(lglmtstk(&lgl->seen));
	assert(!lglsignedmarked(lgl, -root));
	lglsignedmarknpushseen(lgl, -root);
	lglsimpleliftmark(lgl, root, lgl->opts->prbsimpleliftdepth.val);
	count = lglcntstk(&lgl->seen);
	LOG(3, "simple probing lifting marked %d literals for root %d", count, root);
	if (!count)
		return;
	lglsimpleliftcollect(lgl, root, -root, lgl->opts->prbsimpleliftdepth.val);
#ifndef NLGLOG
	{
		int units = lglcntstk(&lgl->sprb->units);
		int eqs = lglcntstk(&lgl->sprb->eqs);
		assert(!(eqs & 1));
		eqs >>= 1;
		LOG(3, "found %d units through simple lifting for %d", units, root);
		LOG(3, "found %d equivalences through simple lifting for %d", eqs, root);
	}
#endif
	lglpopnunmarkstk(lgl, &lgl->seen);
}

static void
lglsimpleprobelit(LGL* lgl, int lit)
{
	int blit, tag, other, other2, red, lidx, cls[4];
	const int *p, *w, *eow, *c;
	HTS* hts;
	lgl->stats->prb.simple.probed++;
	assert(!lglval(lgl, lit));
	lglclnstk(&lgl->sprb->units);
	lglclnstk(&lgl->sprb->impls);
	if (!lglwrktouched(lgl, -lit))
		lglsimplelift(lgl, lit);
	LOG(2, "next simple probing %d", lit);
	hts = lglhts(lgl, lit);
	if (!hts->count)
		return;
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow; p++) {
		if (INCSTEPS(prb.simple.steps) >= lgl->limits->prb.steps)
			goto DONE;
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == BINCS || tag == LRGCS)
			continue;
		if (tag == TRNCS) {
			other = blit >> RMSHFT;
			if (lglval(lgl, other))
				continue;
			other2 = *p;
			if (lglval(lgl, other2))
				continue;
			cls[0] = lit, cls[1] = other, cls[2] = other2, cls[3] = 0;
			lglsimpleprobehbr(lgl, lit, cls);
		} else {
			assert(tag == OCCS);
			red = (blit & REDCS);
			lidx = (blit >> RMSHFT);
			c = lglidx2lits(lgl, red, lidx);
			lglsimpleprobehbr(lgl, lit, c);
		}
	}
DONE:
	assert(lglmtstk(&lgl->seen));
	lglpopnunmarkstk(lgl, &lgl->seen);
}

static void
lglprtsimpleproberem(LGL* lgl)
{
	int idx, ret = 0, rem = 0, sum;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		if (lglavar(lgl, idx)->donotsimpleprobe)
			ret++;
		else
			rem++;
	}
	if (rem) {
		sum = rem + ret;
		lglprt(lgl,
			   1,
			   "[simpleprobe-%d] %d variables remain %.0f%% (%d retained %.0f%%)",
			   lgl->stats->prb.simple.count,
			   rem,
			   lglpcnt(rem, sum),
			   ret,
			   lglpcnt(ret, sum));
	} else {
		lglprt(lgl, 1, "[simpleprobe-%d] fully completed simple probing", lgl->stats->prb.simple.count);
		for (idx = 2; idx < lgl->nvars; idx++)
			lglavar(lgl, idx)->donotsimpleprobe = 0;
	}
}

static int64_t
lglirrlits(LGL* lgl)
{
	return lgl->stats->irr.lits.cur;
}

static int64_t
lglredlits(LGL* lgl)
{
	int64_t res = 2 * lgl->stats->red.bin + 3 * lgl->stats->red.trn;
	int glue;
	for (glue = 0; glue <= MAXGLUE; glue++) {
		res += lglcntstk(lgl->red + glue);
		res -= 2 * lgl->stats->lir[glue].clauses;
	}
	return res;
}

static int
lglszpenaux(LGL* lgl, int litstoo, int redtoo)
{
	int64_t size64;
	int res;
	size64 = lgl->stats->irr.clauses.cur;
	if (litstoo)
		size64 += lglirrlits(lgl) / 4;
	if (redtoo) {
		size64 += lgl->stats->irr.clauses.cur;
		if (litstoo)
			size64 += lglredlits(lgl) / 4;
	}
	assert(size64 >= 0);
	size64 /= lgl->opts->sizepen.val;
	if (size64 >= INT_MAX)
		res = 32;
	else
		res = lglceild((int)size64);
	if (res < 0)
		res = 0;
	if (res > lgl->opts->sizemaxpen.val)
		res = lgl->opts->sizemaxpen.val;
	return res;
}

static int
lglszpen(LGL* lgl)
{
	return lglszpenaux(lgl, 0, 0);
}

#ifndef NLGLYALSAT

static int
lglitszpen(LGL* lgl)
{
	int res = lglceild(lgl->stats->irr.lits.cur / lgl->opts->sizepen.val);
	if (res < 0)
		res = 0;
	if (res > lgl->opts->sizemaxpen.val + 2)
		res = lgl->opts->sizemaxpen.val + 2;
	return res;
}

#endif

static int64_t
lglvisearch(LGL* lgl)
{
	int64_t res = lgl->stats->visits.search;
	assert(res >= lgl->limits->inc.visits);
	res -= lgl->limits->inc.visits;
	return res;
}

static void
lglsetprbsimplelim(LGL* lgl)
{
	int64_t limit, irrlim;
	int pen, szpen;
	if (lgl->opts->prbsimplertc.val || lgl->opts->prbrtc.val) {
		lgl->limits->prb.steps = LLMAX;
		lglprt(lgl, 1, "[simpleprobe-%d] really no limit (run to completion)", lgl->stats->prb.simple.count);
	} else {
		limit = (lgl->opts->prbsimplereleff.val * lglvisearch(lgl)) / 1000;
		if (limit < lgl->opts->prbsimplemineff.val)
			limit = lgl->opts->prbsimplemineff.val;
		if (lgl->opts->prbsimplemaxeff.val >= 0 && limit > lgl->opts->prbsimplemaxeff.val)
			limit = lgl->opts->prbsimplemaxeff.val;
		if (lgl->stats->prb.simple.count <= 1 && lgl->opts->boost.val && lgl->opts->prbsimpleboost.val > 1) {
			lglprt(lgl,
				   1,
				   "[simpleprobe-%d] boosting simple probing limit by %d",
				   lgl->stats->prb.simple.count,
				   lgl->opts->prbsimpleboost.val);
			limit *= lgl->opts->prbsimpleboost.val;
		}
		limit >>= (pen = lgl->limits->prb.simple.pen + (szpen = lglszpen(lgl)));
		irrlim = (2 * lgl->stats->irr.clauses.cur) >> szpen;
		if (lgl->opts->irrlim.val && limit < irrlim) {
			limit = irrlim;
			lglprt(lgl,
				   1,
				   "[simpleprobe-%d] limit %lld based on %d irredundant clauses penalty %d",
				   lgl->stats->prb.simple.count,
				   (LGLL)limit,
				   lgl->stats->irr.clauses.cur,
				   szpen);
		} else
			lglprt(lgl,
				   1,
				   "[simpleprobe-%d] limit %lld penalty %d = %d + %d",
				   lgl->stats->prb.simple.count,
				   (LGLL)limit,
				   pen,
				   lgl->limits->prb.simple.pen,
				   szpen);
		lgl->limits->prb.steps = lgl->stats->prb.simple.steps + limit;
	}
}

static int
lgldelaying(LGL* lgl, const char* name, int* delptr)
{
	if (!*delptr)
		return 0;
	*delptr -= 1;
	lglprt(lgl, 2, "[%s-delayed] skipping this scheduled invocation and %d more", name, *delptr);
	return 1;
}

static int
lglsimpleprobe(LGL* lgl)
{
	int deltarem, nunits, hbrs, neqs, oldrem = lglrem(lgl);
	int lit, success, nvars;

	if (lgldelaying(lgl, "simpleprobe", &lgl->limits->prb.simple.del.rem))
		return 1;

	lglstart(lgl, &lgl->times->prb.simple);
	lgl->stats->prb.simple.count++;

	assert(!lgl->simp && !lgl->probing && !lgl->simpleprobing);
	lgl->simp = lgl->probing = lgl->simpleprobing = 1;

	NEW(lgl->sprb, 1);

	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	lglgc(lgl);
	assert(lgl->frozen);
	lgldense(lgl, 0);
	lglsimpleprobeinit(lgl);
	lglsetprbsimplelim(lgl);
	neqs = hbrs = nunits = 0;

	nvars = lgl->nvars;
	NEW(lgl->sprb->spes, 2 * nvars);
	lgl->sprb->spes += nvars;

	while (!lgl->mt && (lit = lglwrknext(lgl))) {
		if (INCSTEPS(prb.simple.steps) >= lgl->limits->prb.steps)
			break;
		if (!lglisfree(lgl, lit))
			continue;
		if (lglavar(lgl, lit)->equiv)
			continue;
		lglsimpleprobelit(lgl, lit);
		if (lgl->mt)
			break;
		lglchkirrstats(lgl);
		nunits += lglsimpleprobeunits(lgl);
		if (lgl->mt)
			break;
		hbrs += lglsimpleprobeimpls(lgl);
		if (lgl->mt)
			break;
		neqs += lglsimpleprobeqs(lgl);
		if (!lglflush(lgl))
			break;
		if (lglterminate(lgl))
			break;
		if (!lglsyncunits(lgl))
			break;
	}

	lglsimpleprobereset(lgl, nvars);
	lglsparse(lgl);
	if (!lgl->mt)
		lgldecomp(lgl);

	DEL(lgl->sprb, 1);

	deltarem = oldrem - lglrem(lgl);
	success = deltarem || hbrs || nunits;
	LGLUPDPEN(prb.simple, deltarem);
	lglprtsimpleproberem(lgl);

	lglprt(lgl,
		   1 + !success,
		   "[simpleprobe-%d] removed %d variables, found %d hbrs, %d units",
		   lgl->stats->prb.simple.count,
		   deltarem,
		   hbrs,
		   nunits);

	assert(lgl->simp && lgl->probing && lgl->simpleprobing);
	lgl->simp = lgl->probing = lgl->simpleprobing = 0;
	lglrep(lgl, 2, 'p');
	lglstop(lgl);
	return !lgl->mt;
}

static int
lglmvbcls(LGL* lgl, int a, int b)
{
	int res = 0;
	assert(abs(a) != abs(b));
	assert(!lglval(lgl, a));
	assert(!lglval(lgl, b));
	assert(lglmtstk(&lgl->clause));
	lglpushstk(lgl, &lgl->clause, a);
	lglpushstk(lgl, &lgl->clause, b);
	lglpushstk(lgl, &lgl->clause, 0);
	if (!lglsimpleprobeclausexists(lgl)) {
		LOG(2, "moving redundant binary clause %d %d", a, b);
		lgldrupligaddcls(lgl, REDCS);
		lgladdcls(lgl, REDCS, 0, 1);
		res = 1;
	}
	lglclnstk(&lgl->clause);
	lgl->stats->moved.bin++;
	return res;
}

static int
lglrmvbcls(LGL* lgl, int a, int b)
{
	lglrmbcls(lgl, a, b, 0);
	if (lgl->opts->move.val)
		return lglmvbcls(lgl, a, b);
	return 0;
}

static int
lglmvtcls(LGL* lgl, int a, int b, int c)
{
	int res = 0;
	assert(abs(a) != abs(b));
	assert(abs(a) != abs(c));
	assert(abs(b) != abs(c));
	assert(lglmtstk(&lgl->clause));
	lglpushstk(lgl, &lgl->clause, a);
	lglpushstk(lgl, &lgl->clause, b);
	lglpushstk(lgl, &lgl->clause, c);
	lglpushstk(lgl, &lgl->clause, 0);
	if (!lglsimpleprobeclausexists(lgl)) {
		LOG(2, "moving redundant ternary clause %d %d %d", a, b, c);
		lgldrupligaddcls(lgl, REDCS);
		lgladdcls(lgl, REDCS, 0, 1);
		res = 1;
	}
	lglclnstk(&lgl->clause);
	lgl->stats->moved.trn++;
	return res;
}

static int
lglrmvtcls(LGL* lgl, int a, int b, int c)
{
	assert(abs(a) != abs(b));
	assert(abs(a) != abs(c));
	assert(abs(b) != abs(c));
	lglrmtcls(lgl, a, b, c, 0);
	if (lgl->opts->move.val >= 2)
		return lglmvtcls(lgl, a, b, c);
	return 0;
}

static int
lglwaiting(LGL* lgl, const char* str, int wait)
{
	if (!wait)
		return 0;
	if (!lgl->wait)
		return 0;
	if (wait >= 2 && lgl->opts->elim.val && !lgl->elmrtc) {
		if (str)
			lglprt(lgl, 2, "[%s-waiting] on variable elimination to be completed", str);
		return 1;
	}
	if (wait >= 1 && lgl->opts->block.val && !lgl->blkrtc) {
		if (str)
			lglprt(lgl, 2, "[%s-waiting] on blocked clause elimination to be completed", str);
		return 1;
	}
	return 0;
}

static void
lglbasicate(LGL* lgl, int lit)
{
	const int druplig = lgl->opts->druplig.val;
	int blit, tag, red, other, other2, lidx;
	const int *w, *eow, *p, *c, *l;
	int nonfalse, numtrue, val;
	HTS* hts;
	if (!lgl->opts->bate.val)
		return;
	if (lglwaiting(lgl, 0, lgl->opts->batewait.val))
		return;
RESTART:
	hts = lglhts(lgl, -lit);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		red = blit & REDCS;
		if (red)
			continue;
		if (tag == BINCS)
			continue;
		if (tag == TRNCS) {
			other = blit >> RMSHFT;
			numtrue = 0;
			if ((val = lglval(lgl, other)) < 0)
				continue;
			if (val > 0) {
				if (!lglevel(lgl, other))
					continue;
				if (lgltd(lgl, other)->irr)
					numtrue++;
			}
			other2 = *p;
			if ((val = lglval(lgl, other2)) < 0)
				continue;
			if (val > 0) {
				if (!lglevel(lgl, other2))
					continue;
				if (lgltd(lgl, other2)->irr)
					numtrue++;
			}
			if (!numtrue)
				continue;
			LOG(2, "basic ATE ternary clause %d %d %d", -lit, other, other2);
			lgl->stats->prb.basic.ate.trnr++;
			if (!lglrmvtcls(lgl, -lit, other, other2))
				lgldrupligdelclsarg(lgl, -lit, other, other2, 0);
			goto RESTART;
		} else {
			assert(tag == OCCS || tag == LRGCS);
			if (tag == LRGCS && lgl->dense)
				continue;
			lidx = (tag == LRGCS) ? *p : (blit >> RMSHFT);
			assert(!red);
			c = lglidx2lits(lgl, 0, lidx);
			numtrue = nonfalse = 0;
			for (l = c; (nonfalse < 2 || !numtrue) && (other = *l); l++) {
				if (other == -lit)
					continue;
				assert(other != lit);
				if ((val = lglval(lgl, other)) >= 0)
					nonfalse++;
				if (val > 0) {
					if (!lglevel(lgl, other)) {
						numtrue = -1;
						break;
					}
					if (lgltd(lgl, other)->irr)
						numtrue++;
				}
			}
			if (numtrue > 0 && nonfalse >= 2) {
				LOGCLS(2, c, "basic ATE large clause");
				lgl->stats->prb.basic.ate.lrg++;
				if (druplig)
					lgldrupligdelclsaux(lgl, c);
				lglrmlcls(lgl, lidx, 0);
				goto RESTART;
			}
		}
	}
}

static void
lglbasicatestats(LGL* lgl)
{
	int n = lgl->stats->prb.basic.ate.lrg + lgl->stats->prb.basic.ate.trnr;
	int d = n - lgl->stats->prb.basic.lastate;
	assert(d >= 0);
	if (d <= 0)
		return;
	lgl->stats->prb.basic.ate.count++;
	lglprt(lgl, 1, "[bate-%d] eliminated %d asymmetric tautologies during probing", lgl->stats->prb.basic.ate.count, d);
	lgl->stats->prb.basic.lastate = n;
}

static void
lglbasicprobelit(LGL* lgl, int root)
{
	int old, ok, dom, lit, val;
	Stk lift, saved;
	const int* p;
	CLR(lift);
	CLR(saved);
	assert(lgl->simp);
	assert(lgl->probing || lgl->cceing);
	assert(!lgl->level);
	LOG(2, "next probe %d positive phase", root);
	assert(!lgl->level);
	if (lgl->treelooking)
		lgl->stats->prb.treelook.probed++;
	else if (lgl->cceing)
		lgl->stats->cce.probed++;
	else
		assert(lgl->basicprobing), lgl->stats->prb.basic.probed++;
	if (lglrand(lgl) & (1 << 12))
		root = -root;
	lgliassume(lgl, root);
	old = lgl->next;
	ok = lglbcp(lgl);
	dom = 0;
	if (ok) {
		lglclnstk(&saved);
		lglbasicate(lgl, root);
		for (p = lgl->trail.start + old; p < lgl->trail.top; p++) {
			lit = *p;
			if (lit == root)
				continue;
			lglpushstk(lgl, &saved, lit);
		}
	} else
		dom = lglprbana(lgl, root);
	lglbacktrack(lgl, 0);
	if (!ok) {
		LOG(1, "failed literal %d on probing", dom, root);
		lglpushstk(lgl, &lift, -dom);
		goto MERGE;
	}
	LOG(2, "next probe %d negative phase", -root);
	assert(!lgl->level);
	if (lgl->treelooking)
		lgl->stats->prb.treelook.probed++;
	else if (lgl->cceing)
		lgl->stats->cce.probed++;
	else
		assert(lgl->basicprobing), lgl->stats->prb.basic.probed++;
	lgliassume(lgl, -root);
	ok = lglbcp(lgl);
	if (ok) {
		lglbasicate(lgl, -root);
		for (p = saved.start; p < saved.top; p++) {
			lit = *p;
			val = lglval(lgl, lit);
			if (val <= 0)
				continue;
			if (lgl->treelooking)
				lgl->stats->prb.treelook.lifted++;
			else if (lgl->cceing)
				lgl->stats->cce.lifted++;
			else
				assert(lgl->basicprobing), lgl->stats->prb.basic.lifted++;
			lglpushstk(lgl, &lift, lit);
			LOG(2, "lifted %d", lit);
		}
	} else
		dom = lglprbana(lgl, -root);
	lglbacktrack(lgl, 0);
	if (!ok) {
		LOG(1, "failed literal %d on probing %d", dom, -root);
		lglpushstk(lgl, &lift, -dom);
	}
MERGE:
	while (!lglmtstk(&lift)) {
		lit = lglpopstk(&lift);
		val = lglval(lgl, lit);
		if (val > 0)
			continue;
		if (val < 0)
			goto EMPTY;
		if (lgl->opts->druplig.val) {
			lgldrupligaddclsarg(lgl, REDCS, -root, lit, 0);
			lgldrupligaddclsarg(lgl, REDCS, root, lit, 0);
		}
		lglunit(lgl, lit);
		if (lgl->opts->druplig.val) {
			lgldrupligdelclsarg(lgl, -root, lit, 0);
			lgldrupligdelclsarg(lgl, root, lit, 0);
		}
		if (lgl->treelooking)
			lgl->stats->prb.treelook.failed++;
		else if (lgl->cceing)
			lgl->stats->cce.failed++;
		else
			assert(lgl->basicprobing), lgl->stats->prb.basic.failed++;
		if (lglbcp(lgl))
			continue;
	EMPTY:
		LOG(1, "empty clause after propagating lifted and failed literals");
		lglmt(lgl);
	}
	lglrelstk(lgl, &lift);
	lglrelstk(lgl, &saved);
}

static TVar*
lgltvar(LGL* lgl, int lit)
{
	int idx = abs(lit);
	assert(2 <= idx && idx < lgl->nvars);
	assert(lgl->tlk);
	return lgl->tlk->tvars + idx;
}

static void
lglsignedtlmark(LGL* lgl, int lit)
{
	TVar* tv = lgltvar(lgl, lit);
	unsigned bit = 1u << (lit < 0);
	if (tv->mark & bit)
		return;
	tv->mark |= bit;
}

static int
lglsignedtlmarked(LGL* lgl, int lit)
{
	TVar* tv = lgltvar(lgl, lit);
	unsigned bit = 1u << (lit < 0);
	return tv->mark & bit;
}

static void
lgltlenq(LGL* lgl, int start)
{
	int lit, blit, tag, other, oldcount, newcount, *stk;
	unsigned delta, i, j, mod;
	const int *p, *w, *eos;
	HTS* hts;
	assert(!lglsignedtlmarked(lgl, start));
	lglsignedtlmark(lgl, start);
	lglpushstk(lgl, &lgl->tlk->stk, start);
	while (!lglmtstk(&lgl->tlk->stk)) {
		lit = lglpopstk(&lgl->tlk->stk);
		LOG(2, "tree-look enqueue %d%s", lit, lit == start ? " start" : "");
		if (lit) {
			assert(!lglval(lgl, lit));
			assert(lglsignedtlmarked(lgl, lit));
			lglpushstk(lgl, &lgl->tlk->seen, lit);
			lglpushstk(lgl, &lgl->tlk->stk, 0);
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eos = w + hts->count;
			oldcount = lglcntstk(&lgl->tlk->stk);
			for (p = w; p < eos; p++) {
				INCSTEPS(prb.treelook.steps);
				blit = *p;
				tag = blit & MASKCS;
				if (tag == OCCS)
					continue;
				if (tag == TRNCS || tag == LRGCS) {
					p++;
					continue;
				}
				assert(tag == BINCS);
				other = -(blit >> RMSHFT);
				if (lglval(lgl, other))
					assert(lglval(lgl, other) < 0);
				else if (!lglsignedtlmarked(lgl, other)) {
					lglsignedtlmark(lgl, other);
					lglpushstk(lgl, &lgl->tlk->stk, other);
				}
			}
			newcount = lglcntstk(&lgl->tlk->stk);
			delta = newcount - oldcount;
			if ((mod = delta) <= 1)
				continue;
			stk = lgl->tlk->stk.start;
			for (i = 0; i < delta - 1; i++, mod--) {
				assert(mod > 0);
				j = i + (lglrand(lgl) % mod);
				assert(i <= j && j < delta);
				if (i == j)
					continue;
				SWAP(int, stk[i + oldcount], stk[j + oldcount]);
			}
		} else
			lglpushstk(lgl, &lgl->tlk->seen, 0);
	}
}

static int
lglisroot(LGL* lgl, int lit)
{
	return !lglhasbins(lgl, lit);
}

static void
lgltlunmarkall(LGL* lgl)
{
	int idx;
	for (idx = 2; idx < lgl->nvars; idx++)
		lgl->tlk->tvars[idx].mark = 0;
}

static int
lgltlschedlit(LGL* lgl, int lit)
{
	if (INCSTEPS(prb.treelook.steps) > lgl->limits->prb.steps)
		return 0;
	if (!lglisfree(lgl, lit))
		return 1;
	if (lglsignedtlmarked(lgl, lit))
		return 1;
	if (!lglisroot(lgl, -lit))
		return 1;
	if (!lgl->tlk->lkhd && lglavar(lgl, lit)->donotreelook)
		return 1;
	if (lgl->opts->treelook.val >= 2) {
		lglbasicprobelit(lgl, -lit);
		if (lgl->mt)
			return 0;
		if (lglterminate(lgl))
			return 0;
		if (!lglisfree(lgl, lit))
			return 1;
		if (!lglisroot(lgl, -lit))
			return 1;
		if (!lglhasbins(lgl, lit))
			return 1;
	}
	lgltlenq(lgl, lit);
	return 1;
}

static int
lgltlschedanylit(LGL* lgl, int lit)
{
	if (INCSTEPS(prb.treelook.steps) > lgl->limits->prb.steps)
		return 0;
	if (!lglisfree(lgl, lit))
		return 1;
	if (lglsignedtlmarked(lgl, lit))
		return 1;
	if (!lgl->tlk->lkhd && lglavar(lgl, lit)->donotreelook)
		return 1;
	if (lgl->opts->treelook.val >= 2) {
		lglbasicprobelit(lgl, -lit);
		if (lgl->mt)
			return 0;
		if (lglterminate(lgl))
			return 0;
		if (!lglisfree(lgl, lit))
			return 1;
	}
	lgltlenq(lgl, lit);
	return 1;
}

static void
lgltlsched(LGL* lgl)
{
	int idx, round, count, *p;
	LOG(1, "scheduling tree-look literals");
	for (round = 0; !lgl->mt && round < 2; round++) {
		assert(lglmtstk(&lgl->tlk->seen));
		assert(lglmtstk(&lgl->tlk->stk));
		if (lglrandlitrav(lgl, lgltlschedlit))
			lglrandlitrav(lgl, lgltlschedanylit);
		lgltlunmarkall(lgl);
		lglrelstk(lgl, &lgl->tlk->stk);
		if (!lglmtstk(&lgl->tlk->seen))
			break;
		for (idx = 2; idx < lgl->nvars; idx++)
			lglavar(lgl, idx)->donotreelook = 0;
	}
	lglfitstk(lgl, &lgl->tlk->seen);
	count = 0;
	for (p = lgl->tlk->seen.start; p < lgl->tlk->seen.top; p++)
		if (*p)
			count++;
	lglprt(lgl,
		   1,
		   "[treelook-%d] scheduled %d literals %.0f%%",
		   lgl->stats->prb.treelook.count,
		   count,
		   lglpcnt(count, 2 * lglrem(lgl)));
	if (!count)
		for (idx = 2; idx < lgl->nvars; idx++)
			lglavar(lgl, idx)->donotreelook = 0;
}

static void
lgltlassign(LGL* lgl, Stk* assignment, int lit)
{
	TVar* tv;
	LOG(2, "tree-look assign %d", lit);
	tv = lgltvar(lgl, lit);
	assert(!tv->val);
	tv->val = lglsgn(lit) * (1 + lglcntstk(assignment));
	lglpushstk(lgl, assignment, lit);
	assert(tv->val);
	assert(lglsgn(tv->val) == lglsgn(lit));
}

static void
lgltlunassign(LGL* lgl, int lit)
{
	TVar* tv;
	LOG(2, "tree-look unassign %d", lit);
	tv = lgltvar(lgl, lit);
	assert(tv->val);
	assert(lglsgn(tv->val) == lglsgn(lit));
	tv->val = 0;
}

static int
lgltlval(LGL* lgl, int lit)
{
	TVar* tv = lgltvar(lgl, lit);
	int res;
	if (!tv->val)
		return 0;
	res = lglsgn(tv->val);
	if (lit < 0)
		res = -res;
	return res;
}

static void
lglincreducedptr(LGL* lgl, Flt* reducedptr, int size)
{
	Flt reduced = *reducedptr, inc = lglflt(-size, 1);
	reduced = lgladdflt(reduced, inc);
	LOG(2,
		"reduced score incremented to %s from %s by %s (size %d)",
		lglflt2str(lgl, reduced),
		lglflt2str(lgl, *reducedptr),
		lglflt2str(lgl, inc),
		size);
	*reducedptr = reduced;
	(void)lgl;
}

static int
lgltlbcp(LGL* lgl, Stk* assignment, Flt* reducedptr, int dom)
{
	int next = lglcntstk(assignment), next2 = next, lit, size, implied;
	int blit, tag, other, other2, val, lidx, red, found, trivial;
	int numnonfalse, numfalse, firstfalse, litval, skip;
	const int *p, *w, *eos, *c, *q;
	HTS* hts;
	if (lglval(lgl, dom))
		return 1;
	assert(!lgltlval(lgl, dom));
	lgltlassign(lgl, assignment, dom);
	lgl->stats->prb.treelook.probed++;
	LOG(2, "starting tree-look bcp on %d", dom);
	for (;;) {
		if (next2 < (int)lglcntstk(assignment)) {
			assert(lgl->simp);
			INCSTEPS(props.simp);
			if (lgl->lkhd)
				INCSTEPS(props.lkhd);
			lit = lglpeek(assignment, next2++);
			assert(lgltlval(lgl, lit) > 0);
			LOG(2, "tree-look binary clause bcp on %d", lit);
			hts = lglhts(lgl, -lit);
			w = lglhts2wchs(lgl, hts);
			eos = w + hts->count;
			for (p = w; p < eos; p++) {
				assert(lgl->simp);
				lgl->stats->visits.simp++;
				if (lgl->lkhd)
					lgl->stats->visits.lkhd++;
				INCSTEPS(prb.treelook.steps);
				blit = *p;
				tag = blit & MASKCS;
				if (tag == OCCS)
					continue;
				if (tag == TRNCS || tag == LRGCS) {
					p++;
					continue;
				}
				assert(tag == BINCS);
				other = blit >> RMSHFT;
				val = lglval(lgl, other);
				if (val < 0)
					return 0;
				if (val > 0)
					continue;
				val = lgltlval(lgl, other);
				if (val < 0)
					return 0;
				if (reducedptr)
					lglincreducedptr(lgl, reducedptr, 2);
				if (val > 0)
					continue;
				lgltlassign(lgl, assignment, other);
			}
		} else if (next < (int)lglcntstk(assignment)) {
			lit = lglpeek(assignment, next++);
			litval = lgltlval(lgl, lit);
			assert(litval > 0);
			LOG(2, "tree-look non-binary clause bcp on %d", lit);
			hts = lglhts(lgl, -lit);
			w = lglhts2wchs(lgl, hts);
			eos = w + hts->count;
			for (p = w; p < eos; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == LRGCS || tag == TRNCS)
					p++;
				if (tag == BINCS || tag == LRGCS)
					continue;
				INCSTEPS(prb.treelook.steps);
				assert(lglmtstk(&lgl->clause));
				skip = trivial = 0;
				if (tag == TRNCS) {
					other = blit >> RMSHFT;
					other2 = *p;
					if (lglval(lgl, other) > 0 || lglval(lgl, other2) > 0)
						skip = 1;
					else {
						if (lgltlval(lgl, other) > 0 || lgltlval(lgl, other2) > 0)
							trivial = 1;
						lglpushstk(lgl, &lgl->clause, -lit), lglpushstk(lgl, &lgl->clause, other),
							lglpushstk(lgl, &lgl->clause, other2);
					}
				} else {
					assert(tag == OCCS);
					red = blit & REDCS;
					lidx = blit >> RMSHFT;
					found = trivial = 0;
					c = lglidx2lits(lgl, red, lidx);
					INCSTEPS(prb.treelook.steps);
					for (q = c; !skip && (other = *q); q++) {
						if (other >= NOTALIT)
							skip = 1;
						else if (lglval(lgl, other) > 0)
							skip = 1;
						else {
							if (other == -lit)
								assert(!found), found++;
							if (lgltlval(lgl, other) > 0)
								trivial = 1;
							lglpushstk(lgl, &lgl->clause, other);
						}
					}
					assert(skip || found);
				}
				numnonfalse = numfalse = size = implied = 0;
				firstfalse = 1;
				if (!skip) {
					INCSTEPS(prb.treelook.steps);
					for (q = lgl->clause.start; q < lgl->clause.top; q++) {
						other = *q;
						if (lglval(lgl, other) < 0)
							continue;
						size++;
						if ((val = lgltlval(lgl, other)) < 0) {
							if (reducedptr && -val < litval)
								firstfalse = 0;
							numfalse++;
							continue;
						}
						numnonfalse++;
						if (implied == NOTALIT)
							continue;
						else if (implied)
							implied = NOTALIT;
						else
							implied = other;
					}
				}
				lglclnstk(&lgl->clause);
				if (skip)
					continue;
				if (reducedptr && firstfalse)
					lglincreducedptr(lgl, reducedptr, size);
				if (trivial)
					continue;
				if (!numnonfalse) {
					assert(!implied);
					return 0;
				}
				assert(implied);
				if (implied == NOTALIT)
					continue;
				assert(numnonfalse == 1);
				if (!numfalse)
					continue; // TODO remove?
				INCSTEPS(prb.treelook.steps);
				if (tag == TRNCS)
					lgl->stats->hbr.trn++;
				else
					lgl->stats->hbr.lrg++;
				LOG(2, "tree-look hyper binary resolvent %d %d", -dom, implied);
				lgldrupligaddclsarg(lgl, REDCS, -dom, implied, 0);
				(void)lglwchbin(lgl, -dom, implied, REDCS);
				(void)lglwchbin(lgl, implied, -dom, REDCS);
				lgl->stats->red.bin++, assert(lgl->stats->red.bin > 0);
				lgl->stats->hbr.cnt++;
				lgl->stats->prgss++;
				lgltlassign(lgl, assignment, implied);
				assert(next > 0), next--; // force to redo this trn/lrg literal
				break;					  // and continue outer 'next2' prop on 'implied'
			}
		} else
			break;
	}
	if (reducedptr)
		assert(lgl->tlk->lkhd), lgl->tlk->lkhd[dom] = *reducedptr;
	return 1;
}

static void
lgltreelooklit(LGL* lgl, Stk* a, Ftk* r, int lit)
{
	Flt reduced;
	LOG(2, "treelook dequeue %d", lit);
	if (lit) {
		if (lgl->opts->treelook.val >= 2 && lglmtstk(a) && lglisfree(lgl, lit))
			lglbasicprobelit(lgl, -lit);
		if (r) {
			reduced = lglmtftk(r) ? FLTMIN : lgltopftk(r);
			lglpushftk(lgl, r, reduced);
		}
		lglpushstk(lgl, a, 0);
		if (lgl->mt || !lglisfree(lgl, lit))
			return;
		if (lgltlval(lgl, -lit) > 0 || (!lgltlval(lgl, lit) && !lgltlbcp(lgl, a, r ? &reduced : 0, lit))) {
			int tmp;
			LOG(1, "tree-look failed literal %d", lit);
			if (!(tmp = lglval(lgl, lit))) {
				lgl->stats->prb.treelook.failed++;
				lglunit(lgl, -lit);
				if (!lglbcp(lgl)) {
					LOG(1, "inconsistent tree-look failed literal");
					if (!lgl->mt)
						lglmt(lgl);
				}
			} else if (tmp > 0) {
				lgl->stats->prb.treelook.failed++;
				LOG(1, "tree-look failed literal literal %d inconsistent", lit);
				if (!lgl->mt)
					lglmt(lgl);
			} else
				LOG(1, "tree-look failed literal %d already found", lit);
		}
		if (r) {
			assert(r->start < r->top);
			assert(r->top[-1] <= reduced);
			r->top[-1] = reduced;
		}
	} else {
		if (r)
			lglpopftk(r);
		while ((lit = lglpopstk(a)))
			lgltlunassign(lgl, lit);
	}
}

static int64_t
lglsetprbtreelooklim(LGL* lgl, int* lkhdresptr)
{
	int szpen, lastpen, pen, boost;
	int64_t limit, irrlim;
	if (lgl->opts->treelookrtc.val || lgl->opts->prbrtc.val) {
		limit = lgl->limits->prb.steps = LLMAX;
		lglprt(lgl, 1, "[treelook-%d] really no limit (run to completion)", lgl->stats->prb.treelook.count);
	} else if (lkhdresptr && lgl->opts->treelookfull.val) {
		limit = lgl->limits->prb.steps = LLMAX;
		lglprt(lgl, 1, "[treelook-%d] unlimited look-ahead requested", lgl->stats->prb.treelook.count);
	} else {
		limit = (lgl->opts->treelookreleff.val * lglvisearch(lgl)) / 2000;
		if (limit < lgl->opts->treelookmineff.val)
			limit = lgl->opts->treelookmineff.val;
		if (lgl->opts->treelookmaxeff.val >= 0 && limit > lgl->opts->treelookmaxeff.val)
			limit = lgl->opts->treelookmaxeff.val;
		assert(lgl->tlk);
		szpen = lglszpen(lgl);
		lastpen = lgl->tlk->lkhd ? lgl->limits->lkhdpen : lgl->limits->prb.treelook.pen;
		limit >>= (pen = szpen + lastpen);
		irrlim = (lgl->stats->irr.clauses.cur / 4) >> szpen;
		if (lgl->opts->boost.val && lgl->tlk->lkhd)
			boost = lgl->opts->treelookboost.val;
		else
			boost = 1;
		limit *= boost;
		if (lgl->opts->irrlim.val && limit < irrlim) {
			limit = irrlim;
			lglprt(lgl,
				   1,
				   "[treelook-%d] limit %lld based on %d irredundant clauses penalty %d",
				   lgl->stats->prb.treelook.count,
				   (LGLL)limit,
				   lgl->stats->irr.clauses.cur,
				   szpen);
		} else
			lglprt(lgl,
				   1,
				   "[treelook-%d] limit %lld penalty %d = %d + %d boost %d",
				   lgl->stats->prb.treelook.count,
				   (LGLL)limit,
				   pen,
				   lastpen,
				   szpen,
				   boost);
		if (lkhdresptr)
			lglprt(lgl, 1, "[treelook-%d] limited look-ahead requested", lgl->stats->prb.treelook.count);
		lgl->limits->prb.steps = lgl->stats->prb.treelook.steps + limit;
	}

	return limit;
}

static void
lglclntlvals(LGL* lgl)
{
	int idx;
	for (idx = 2; idx < lgl->nvars; idx++)
		lgl->tlk->tvars[idx].val = 0;
}

#ifndef NDEBUG
static int
lglieliminated(LGL* lgl, int ilit)
{
	return lglelit2ext(lgl, lglexport(lgl, ilit))->eliminated;
}
#endif

static int
lgliblocking(LGL* lgl, int ilit)
{
	return lglelit2ext(lgl, lglexport(lgl, ilit))->blocking;
}

static void
lgltlsetlkhd(LGL* lgl, int* lkhdresptr, int remlits)
{
	LKHD lkhdrescore, lkhdscore, lkhdpos, lkhdneg;
	Flt jwhrescore, jwhscore, jwhpos, jwhneg;
	int idx, lkhdres, phase, elit;
	Ext* ext;
	assert(lkhdresptr);
	lkhdres = 0;
	lkhdrescore = FLTMIN;
	jwhrescore = FLTMIN;
	assert(!lgl->nvars || lgl->tlk->lkhd);
	lgljwh(lgl);
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		assert(!lglieliminated(lgl, idx));
		if (lkhdresptr && lgliblocking(lgl, idx))
			continue;
		if (!remlits) {
			lkhdpos = lgl->tlk->lkhd[idx];
			lkhdneg = lgl->tlk->lkhd[-idx];
			lkhdscore = lglmulflt(lkhdneg, lkhdpos);
			if (lkhdscore > FLTMIN)
				lkhdscore = lgladdflt(lkhdscore, lgladdflt(lkhdneg, lkhdpos));
		} else
			lkhdscore = lkhdpos = lkhdneg = FLTMIN;
		jwhpos = lgl->jwh[lglulit(idx)];
		jwhneg = lgl->jwh[lglulit(-idx)];
		jwhscore = lglmulflt(jwhpos, jwhneg);
		jwhscore = lgladdflt(jwhscore, lgladdflt(jwhpos, jwhneg));
		LOG(1,
			"final tree based look-ahead score [%s,%s] "
			"(pos [%s,%s], neg [%s,%s]) of %d",
			lglflt2str(lgl, lkhdscore),
			lglflt2str(lgl, jwhscore),
			lglflt2str(lgl, lkhdpos),
			lglflt2str(lgl, jwhpos),
			lglflt2str(lgl, lkhdneg),
			lglflt2str(lgl, jwhneg),
			idx);

		// phase with less weighted reductions
		if (lkhdpos > lkhdneg)
			phase = -1;
		else if (lkhdpos < lkhdneg)
			phase = 1;
		// then with more weighted occs
		else if (jwhpos > jwhneg)
			phase = 1;
		else if (jwhpos < jwhneg)
			phase = -1;
		// otherwise negative phase
		else
			phase = -1;

		if (lkhdres) {
			if (lkhdscore < lkhdrescore)
				continue;
			if (lkhdscore == lkhdrescore && jwhscore <= jwhrescore)
				continue;
		}
		lkhdrescore = lkhdscore;
		jwhrescore = jwhscore;
		lkhdres = phase * idx;
	}
	if (lkhdres) {
		elit = lglexport(lgl, lkhdres);
		ext = lglelit2ext(lgl, elit);
		assert(!ext->eliminated);
		assert(!ext->blocking);
		lglprt(lgl,
			   1,
			   "[treelook-%d] best look-ahead %d score [%s,%s]",
			   lgl->stats->prb.treelook.count,
			   lkhdres,
			   lglflt2str(lgl, lkhdrescore),
			   lglflt2str(lgl, jwhrescore));
		if (ext->melted) {
			ext->melted = 0;
			LOG(2, "tree-look-ahead winner external %d not melted anymore", elit);
		} else
			LOG(2, "tree-look-ahead winner external %d was not melted anyhow", elit);
	} else
		LOG(1, "no proper best tree-look-ahead literal found");
	if (lkhdresptr)
		*lkhdresptr = lkhdres;
}

static void
lglgenericreport(LGL* lgl, const char* name, int count, int probed, int nprobes, double start)
{
	int div, mod;
	assert(nprobes >= 0);
	if (!nprobes || lgl->opts->verbose.val < 2)
		return;
	if (lgl->opts->verbose.val == 2)
		div = 10;
	else if (lgl->opts->verbose.val == 3)
		div = 100;
	else
		div = 0;
	mod = div ? (nprobes + div - 1) / div : 1;
	assert(mod > 0);
	if (probed % mod)
		return;
	lglprt(lgl,
		   1,
		   "[%s-%d] probed %d/%d (%.0f%%) %.1f sec %d vars %.0f MB",
		   name,
		   count,
		   probed,
		   nprobes,
		   lglpcnt(probed, nprobes),
		   lglgetime(lgl) - start,
		   lglrem(lgl),
		   lglmb(lgl));
}

static void
lgltreelookreport(LGL* lgl, int probed, int nprobes, double start)
{
	lglgenericreport(lgl, "treelook", lgl->stats->prb.treelook.count, probed, nprobes, start);
}

static int
lgltreelookaux(LGL* lgl, int* lkhdresptr)
{
	int oldhbrs, oldrem, remvarsall, remvarslkhd, remlits, notfullyscheduled;
	int deltavars, newhbrs, success, idx, next, lit, nseen;
	int64_t oldsteps, steps, limit;
	Stk assignment;
	double start;
	Ftk reduced;
	TVar* tv;
	AVar* av;

	if (lgl->mt)
		return 0;
	if (lkhdresptr)
		*lkhdresptr = 0;
	if (!lgl->nvars)
		return 1;

	lglstart(lgl, &lgl->times->prb.treelook);
	lgl->stats->prb.treelook.count++;

	NEW(lgl->tlk, 1);
	NEW(lgl->tlk->tvars, lgl->nvars);

	assert(!lgl->simp && !lgl->probing && !lgl->treelooking);
	lgl->simp = lgl->probing = lgl->treelooking = 1;

	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	oldhbrs = lgl->stats->hbr.cnt;
	oldrem = lglrem(lgl);

	assert(!lgl->tlk->lkhd);
	if (lkhdresptr) {
		NEW(lgl->tlk->lkhd, 2 * lgl->nvars);
		lgl->tlk->lkhd += lgl->nvars;
		assert(lgl->tlk->lkhd);
	}

	limit = lglsetprbtreelooklim(lgl, lkhdresptr);
	oldsteps = lgl->stats->prb.treelook.steps;

	lgltlsched(lgl);

	steps = lgl->stats->prb.treelook.steps - oldsteps;
	lglprt(lgl, 1, "[treelook-%d] scheduling used %lld steps", lgl->stats->prb.treelook.count, (LGLL)steps);

	if (limit != LLMAX) {
		notfullyscheduled = (lgl->stats->prb.treelook.steps >= lgl->limits->prb.steps);
		lgl->limits->prb.steps = lgl->stats->prb.treelook.steps + limit;
		if (notfullyscheduled)
			lglprt(lgl, 1, "[treelook-%d] not fully scheduled", lgl->stats->prb.treelook.count);
		else
			lglprt(lgl, 1, "[treelook-%d] scheduled completely", lgl->stats->prb.treelook.count);
	} else
		notfullyscheduled = 0;

	CLR(assignment);
	CLR(reduced);
	remlits = remvarsall = remvarslkhd = 0;

	nseen = lglcntstk(&lgl->tlk->seen);
	if (!lgl->mt && nseen) {

		(void)lglsetprbtreelooklim(lgl, lkhdresptr);
		oldsteps = lgl->stats->prb.treelook.steps;

		lgldense(lgl, 0);
		next = 0;
		start = lglgetime(lgl);
		lgltreelookreport(lgl, next, nseen, start);
		while (!lgl->mt && !lglterminate(lgl) && lglsyncunits(lgl) && next < nseen &&
			   lgl->stats->prb.treelook.steps < lgl->limits->prb.steps) {
			lit = lglpeek(&lgl->tlk->seen, next++);
			lgltreelooklit(lgl, &assignment, lkhdresptr ? &reduced : 0, lit);
			lgltreelookreport(lgl, next, nseen, start);
		}
		lgltreelookreport(lgl, next, nseen, start);
		lglclntlvals(lgl);
		assert(lgl->stats->prb.treelook.steps >= oldsteps);
		steps = lgl->stats->prb.treelook.steps - oldsteps;
		lglprt(lgl, 1, "[treelook-%d] used %lld steps", lgl->stats->prb.treelook.count, (LGLL)steps);
		while (next < (int)lglcntstk(&lgl->tlk->seen)) {
			lit = lglpeek(&lgl->tlk->seen, next++);
			if (!lit)
				continue;
			if (!lglisfree(lgl, lit))
				continue;
			remlits++;
			tv = lgltvar(lgl, lit);
			tv->val = 1;
		}
		for (idx = 2; idx < lgl->nvars; idx++)
			if (lglisfree(lgl, idx)) {
				tv = lgltvar(lgl, idx);
				av = lglavar(lgl, idx);
				if (tv->val) {
					tv->val = 0;
					if (!av->donotreelook)
						remvarsall++;
					remvarslkhd++;
				} else if (!av->donotreelook)
					av->donotreelook = 1;
			}
		assert(remvarslkhd >= remvarsall);

		if (remvarsall || notfullyscheduled) {
			lglprt(lgl,
				   1,
				   "[treelook-%d] %d literals remain %.0f%%%s",
				   lgl->stats->prb.treelook.count,
				   remlits,
				   lglpcnt(remlits, 2 * lglrem(lgl)),
				   lkhdresptr ? " in look-ahead" : "");
			if (lkhdresptr)
				lglprt(lgl,
					   1,
					   "[treelook-%d] %d look-ahead variables remain %.0f%%",
					   lgl->stats->prb.treelook.count,
					   remvarslkhd,
					   lglpcnt(remvarslkhd, lglrem(lgl)));
			lglprt(lgl,
				   1,
				   "[treelook-%d] %d variables remain to probed next time %.0f%%",
				   lgl->stats->prb.treelook.count,
				   remvarsall,
				   lglpcnt(remvarsall, lglrem(lgl)));
		} else {
			lglprt(lgl,
				   1,
				   "[treelook-%d] fully completed tree based probing%s",
				   lgl->stats->prb.treelook.count,
				   lkhdresptr ? " in look-ahead" : "");
			for (idx = 2; idx < lgl->nvars; idx++)
				lglavar(lgl, idx)->donotreelook = 0;
		}
		lglsparse(lgl);
	}

	DEL(lgl->tlk->tvars, lgl->nvars);
	lglgc(lgl);
	lglrelftk(lgl, &reduced);
	lglrelstk(lgl, &assignment);
	lglrelstk(lgl, &lgl->tlk->seen);
	if (!lgl->mt && lkhdresptr)
		lgltlsetlkhd(lgl, lkhdresptr, remlits || notfullyscheduled);
	if (lgl->tlk->lkhd) {
		lgl->tlk->lkhd -= lgl->nvars;
		DEL(lgl->tlk->lkhd, 2 * lgl->nvars);
	}
	DEL(lgl->tlk, 1);
	newhbrs = lgl->stats->hbr.cnt - oldhbrs;
	if (newhbrs && !lgl->mt && !lkhdresptr)
		lgldecomp(lgl);

	assert(lgl->simp && lgl->probing && lgl->treelooking);
	lgl->simp = lgl->probing = lgl->treelooking = 0;

	deltavars = oldrem - lglrem(lgl);
	success = deltavars || newhbrs;
	if (!lkhdresptr)
		LGLUPDPEN(prb.treelook, deltavars);
	else if (remlits && lgl->limits->lkhdpen < lgl->opts->penmax.val)
		lgl->limits->lkhdpen++;
	else if (!remlits && lgl->limits->lkhdpen)
		lgl->limits->lkhdpen--;
	lglprt(lgl,
		   1 + !success,
		   "[treelook-%d] removed %d variables, found %d hbrs",
		   lgl->stats->prb.treelook.count,
		   deltavars,
		   newhbrs);
	lglrep(lgl, 2, 'p');
	lglstop(lgl);
	lglbasicatestats(lgl);
	return !lgl->mt;
}

static int
lgltreelook(LGL* lgl)
{
	if (lgldelaying(lgl, "treelook", &lgl->limits->prb.treelook.del.rem))
		return 1;
	return lgltreelookaux(lgl, 0);
}

static int
lgljwhlook(LGL* lgl)
{
	Flt best, pos, neg, score;
	int res, idx, elit;
	Ext* ext;
	lgljwh(lgl);
	best = FLTMIN;
	res = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		assert(!lglieliminated(lgl, idx));
		if (lgliblocking(lgl, idx))
			continue;
		pos = lgl->jwh[lglulit(idx)];
		neg = lgl->jwh[lglulit(-idx)];
		score = lglmulflt(pos, neg);
		score = lgladdflt(score, lgladdflt(pos, neg));
		if (res && score <= best)
			continue;
		LOG(1,
			"jwh look-ahead score %s (pos %s, neg %s) of %d",
			lglflt2str(lgl, score),
			lglflt2str(lgl, pos),
			lglflt2str(lgl, neg),
			idx);
		res = (pos > neg) ? idx : -idx;
		best = score;
	}
	if (res) {
		elit = lglexport(lgl, res);
		ext = lglelit2ext(lgl, elit);
		lglprt(lgl, 1, "[jwhlook] best look-ahead %d score %s", res, lglflt2str(lgl, best));
		if (ext->melted) {
			ext->melted = 0;
			LOG(2, "jwh-look-ahead winner external %d not melted anymore", elit);
		} else
			LOG(2, "jwh-look-ahead winner external %d was not melted anyhow", elit);
	} else
		LOG(1, "no proper best jwh-look-ahead literal found");
	return res;
}

static int
lglislook(LGL* lgl)
{
	int64_t best, pos, neg, score;
	int res, idx, elit, *scores;
	Ext* ext;
	scores = lglis(lgl);
	best = res = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		assert(!lglieliminated(lgl, idx));
		if (lgliblocking(lgl, idx))
			continue;
		pos = scores[idx], neg = scores[-idx];
		score = pos * neg + pos + neg;
		assert(0 <= score && pos <= score && neg <= score);
		if (res && score <= best)
			continue;
		LOG(1, "LIS look-ahead score %lld (pos %lld, neg %lld) of %d", (LGLL)score, (LGLL)pos, (LGLL)neg, idx);
		res = (pos > neg) ? idx : -idx;
		best = score;
	}
	scores -= lgl->nvars;
	DEL(scores, 2 * lgl->nvars);
	if (res) {
		elit = lglexport(lgl, res);
		ext = lglelit2ext(lgl, elit);
		assert(!ext->eliminated && !ext->blocking);
		lglprt(lgl, 1, "[lislook] best LIS look-ahead %d score %lld", res, (LGLL)best);
		if (ext->melted) {
			ext->melted = 0;
			LOG(2, "look-ahead winner external %d not melted anymore", elit);
		} else
			LOG(2, "look-ahead winner external %d was not melted anyhow", elit);
	} else
		LOG(1, "no proper best LIS look-ahead literal found");
	return res;
}

static int
lglrelevancelook(LGL* lgl)
{
	int res, elit;
	Ext* ext;
	if (!lgl->unassigned)
		return 0;
	res = lglnextdecision(lgl, 1);
	if (res) {
		elit = lglexport(lgl, res);
		ext = lglelit2ext(lgl, elit);
		if (ext->eliminated || ext->blocking) {
			LOG(1, "no proper relevance look-ahead literal found");
			res = 0;
		} else {
			res = lgldecidephase(lgl, res);
			lglprt(lgl, 1, "[lislook] best relevance look-ahead %d", res);
			if (ext->melted) {
				ext->melted = 0;
				LOG(2, "look-ahead winner external %d not melted anymore", elit);
			} else
				LOG(2, "look-ahead winner external %d was not melted anyhow", elit);
		}
	} else
		LOG(1, "no relevance look-ahead literal found at all");
	return res;
}

static int64_t
lglsatmul64(int64_t a, int64_t b)
{
	assert(a >= 0), assert(b >= 0);
	return (b && (LLMAX / b < a)) ? LLMAX : a * b;
}

static int64_t
lglsatadd64(int64_t a, int64_t b)
{
	assert(a >= 0), assert(b >= 0);
	return (LLMAX - b < a) ? LLMAX : a + b;
}

static int64_t*
lglsumlenscores(LGL* lgl)
{
	int idx, sign, lit, tag, blit, other, other2, red;
	const int *p, *w, *eow, *c;
	int64_t *res, len;
	Val val, tmp, tmp2;
	HTS* hts;
	Stk* s;
	NEW(res, 2 * lgl->nvars);
	res += lgl->nvars;
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			val = lglval(lgl, lit);
			if (val > 0)
				continue;
			hts = lglhts(lgl, lit);
			if (!hts->offset)
				continue;
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (tag == LRGCS)
					continue;
				red = blit & REDCS;
				if (red)
					continue;
				other = blit >> RMSHFT;
				if (abs(other) < abs(lit))
					continue;
				tmp = lglval(lgl, other);
				if (tmp > 0)
					continue;
				if (tag == BINCS) {
					assert(!tmp);
					res[lit] += 2;
					res[other] += 2;
				} else {
					assert(tag == TRNCS);
					other2 = *p;
					if (abs(other2) < abs(lit))
						continue;
					tmp2 = lglval(lgl, other2);
					if (tmp2 > 0)
						continue;
					assert((val > 0) + (tmp > 0) + (tmp2 > 0) == 0);
					assert((val < 0) + (tmp < 0) + (tmp2 < 0) <= 1);
					len = !val + !tmp + !tmp2;
					if (!val)
						res[lit] += len;
					if (!tmp)
						res[other] += len;
					if (!tmp2)
						res[other2] += len;
				}
			}
		}
	s = &lgl->irr;
	for (c = s->start; c < s->top; c = p + 1) {
		p = c;
		if (*p >= NOTALIT)
			continue;
		val = -1;
		len = 0;
		while ((other = *p)) {
			tmp = lglval(lgl, other);
			if (tmp > val)
				val = tmp;
			if (!tmp)
				len++;
			p++;
		}
		if (val > 0)
			continue;
		for (p = c; (other = *p); p++)
			if (!lglval(lgl, other))
				res[other] += len;
	}
	return res;
}

static int
lglsumlenlook(LGL* lgl)
{
	int64_t best, pos, neg, score, *scores;
	int res, idx, elit;
	Ext* ext;
	scores = lglsumlenscores(lgl);
	best = res = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		assert(!lglieliminated(lgl, idx));
		if (lgliblocking(lgl, idx))
			continue;
		pos = scores[idx], neg = scores[-idx];
		score = lglsatadd64(lglsatmul64(pos, neg), lglsatadd64(pos, neg));
		assert(0 <= score), assert(pos <= score), assert(neg <= score);
		if (res && score <= best)
			continue;
		LOG(1, "look-ahead score %lld (pos %lld, neg %lld) of %d", (LGLL)score, (LGLL)pos, (LGLL)neg, idx);
		res = (pos > neg) ? idx : -idx;
		best = score;
	}
	scores -= lgl->nvars;
	DEL(scores, 2 * lgl->nvars);
	if (res) {
		elit = lglexport(lgl, res);
		ext = lglelit2ext(lgl, elit);
		assert(!ext->eliminated && !ext->blocking);
		lglprt(lgl, 1, "[sumlook] best look-ahead %d score %lld", res, (LGLL)best);
		if (ext->melted) {
			ext->melted = 0;
			LOG(2, "look-ahead winner external %d not melted anymore", elit);
		} else
			LOG(2, "look-ahead winner external %d was not melted anyhow", elit);
	} else
		LOG(1, "no proper best look-ahead literal found");
	return res;
}

static int
lglschedbasicprobe(LGL* lgl, Stk* probes, int round)
{
	int idx, res, i, j, donotbasicprobes, keepscheduled;
	assert(lglmtstk(probes));
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		if (lgl->opts->prbasic.val <= 1 && (lglhasbins(lgl, idx) == lglhasbins(lgl, -idx)))
			continue;
		LOG(1, "new probe %d", idx);
		lglpushstk(lgl, probes, idx);
	}
	res = lglcntstk(probes);
	donotbasicprobes = keepscheduled = 0;
	for (i = 0; i < res; i++) {
		idx = lglpeek(probes, i);
		if (!idx)
			continue;
		assert(lglisfree(lgl, idx));
		if (lglavar(lgl, idx)->donotbasicprobe)
			donotbasicprobes++;
		else
			keepscheduled++;
	}
	if (!keepscheduled) {
		for (i = 0; i < res; i++) {
			idx = lglpeek(probes, i);
			if (!idx)
				continue;
			assert(lglisfree(lgl, idx));
			lglavar(lgl, idx)->donotbasicprobe = 0;
			keepscheduled++;
		}
		donotbasicprobes = 0;
	}
	for (i = 0; i < res; i++) {
		idx = lglpeek(probes, i);
		if (!idx)
			continue;
		assert(lglisfree(lgl, idx));
		if (lglavar(lgl, idx)->donotbasicprobe)
			donotbasicprobes++;
		else
			keepscheduled++;
	}
	j = 0;
	for (i = 0; i < res; i++) {
		idx = lglpeek(probes, i);
		if (!idx)
			continue;
		if (!lglavar(lgl, idx)->donotbasicprobe)
			lglpoke(probes, j++, idx);
	}
	lglrststk(probes, (res = j));
	if (!res)
		lglprt(lgl, 2, "[basicprobe-%d-%d] no potential probes found", lgl->stats->prb.basic.count, round);
	else if (!donotbasicprobes)
		lglprt(lgl, 2, "[basicprobe-%d-%d] scheduled all %d potential probes", lgl->stats->prb.basic.count, round, res);
	else
		lglprt(lgl,
			   2,
			   "[basicprobe-%d-%d] scheduled %d probes %.0f%%",
			   lgl->stats->prb.basic.count,
			   round,
			   res,
			   lglpcnt(res, lglrem(lgl)));
	return res;
}

static void
lglsetprbasiclim(LGL* lgl)
{
	int64_t limit, irrlim;
	int pen, szpen;
	if (lgl->opts->prbasicrtc.val || lgl->opts->prbrtc.val) {
		lgl->limits->prb.steps = LLMAX;
		lglprt(lgl, 1, "[basicprobe-%d] really no limit (run to completion)", lgl->stats->prb.basic.count);
	} else {
		limit = (lgl->opts->prbasicreleff.val * lglvisearch(lgl)) / 1000;
		if (limit < lgl->opts->prbasicmineff.val)
			limit = lgl->opts->prbasicmineff.val;
		if (lgl->opts->prbasicmaxeff.val >= 0 && limit > lgl->opts->prbasicmaxeff.val)
			limit = lgl->opts->prbasicmaxeff.val;
		limit >>= (pen = lgl->limits->prb.basic.pen + (szpen = lglszpen(lgl)));
		irrlim = (8 * lgl->stats->irr.clauses.cur) >> szpen;
		if (lgl->opts->irrlim.val && limit < irrlim) {
			limit = irrlim;
			lglprt(lgl,
				   1,
				   "[basicprobe-%d] limit %lld based on %d irredundant clauses penalty %d",
				   lgl->stats->prb.basic.count,
				   (LGLL)limit,
				   lgl->stats->irr.clauses.cur,
				   szpen);
		} else
			lglprt(lgl,
				   1,
				   "[basicprobe-%d] limit %lld penalty %d = %d + %d",
				   lgl->stats->prb.basic.count,
				   (LGLL)limit,
				   pen,
				   lgl->limits->prb.basic.pen,
				   szpen);
		lgl->limits->prb.steps = lgl->stats->prb.basic.steps + limit;
	}
}

static void
lglbasicprobereport(LGL* lgl, int probed, int nprobes, double start)
{
	lglgenericreport(lgl, "basicprobe", lgl->stats->prb.basic.count, probed, nprobes, start);
}

static int
lglbasicprobe(LGL* lgl)
{
#ifndef NLGLOG
	int origprobed = lgl->stats->prb.basic.probed;
#endif
	int origfailed = lgl->stats->prb.basic.failed;
	int origlifted = lgl->stats->prb.basic.lifted;
	int orighbr = lgl->stats->hbr.cnt;
	int root, failed, lifted, units, first, idx;
	int oldrem, deltarem, deltahbr, remprobes;
	int nprobes, success, round, probed;
	int oldhbr, oldfailed, oldlifted;
	Stk probes, lift, saved;
	unsigned pos, delta;
	double start;
	if (!lgl->nvars)
		return 1;
	if (!lgl->opts->probe.val)
		return 1;
	if (lgldelaying(lgl, "basicprobe", &lgl->limits->prb.basic.del.rem))
		return 1;
	lglstart(lgl, &lgl->times->prb.basic);
	lgl->stats->prb.basic.count++;
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	assert(!lgl->simp && !lgl->probing && !lgl->basicprobing);
	lgl->simp = lgl->probing = lgl->basicprobing = 1;
	CLR(lift);
	CLR(probes);
	CLR(saved);
	lglsetprbasiclim(lgl);
	oldfailed = origfailed;
	oldlifted = origlifted;
	oldhbr = lgl->stats->hbr.cnt;
	oldrem = lglrem(lgl);
	round = 0;
RESTART:
	nprobes = lglschedbasicprobe(lgl, &probes, round);
	remprobes = 0;
	if (!nprobes)
		goto DONE;
	pos = lglrand(lgl) % nprobes;
	delta = lglrand(lgl) % nprobes;
	if (!delta)
		delta++;
	probed = 0;
	start = lglgetime(lgl);
	lglbasicprobereport(lgl, probed, nprobes, start);
	while (lglgcd(delta, nprobes) > 1)
		if ((int)++delta == nprobes)
			delta = 1;
	LOG(1, "probing start %u delta %u mod %u", pos, delta, nprobes);
	first = 0;
	while (!lgl->mt) {
		if (lgl->stats->prb.basic.steps >= lgl->limits->prb.steps)
			break;
		if (lglterminate(lgl))
			break;
		if (!lglsyncunits(lgl))
			break;
		assert(pos < (unsigned)nprobes);
		root = probes.start[pos];
		probes.start[pos] = 0;
		if (!root || root == first) {
			lglprt(lgl,
				   1,
				   "[basicprobe-%d-%d] %d sched %.0f%%, %d failed, %d lifted, %d hbrs",
				   lgl->stats->prb.basic.count,
				   round,
				   nprobes,
				   lglpcnt(nprobes, lglrem(lgl)),
				   lgl->stats->prb.basic.failed - oldfailed,
				   lgl->stats->prb.basic.lifted - oldlifted,
				   lgl->stats->hbr.cnt - oldhbr);
			for (idx = 2; idx < lgl->nvars; idx++)
				lglavar(lgl, idx)->donotbasicprobe = 0;
			break;
		}
		lglavar(lgl, root)->donotbasicprobe = 1;
		if (!first)
			first = root;
		pos += delta;
		if ((int)pos >= nprobes)
			pos -= nprobes;
		if (!lglisfree(lgl, root))
			continue;
		lglbasicprobereport(lgl, ++probed, nprobes, start);
		lglbasicprobelit(lgl, root);
	}
	lglbasicprobereport(lgl, probed, nprobes, start);
	if (!lgl->mt) {
		if (lgl->stats->prb.basic.steps >= lgl->limits->prb.steps) {
			while (!lglmtstk(&probes))
				if ((idx = lglpopstk(&probes)) && lglisfree(lgl, idx))
					remprobes++;
			lglprt(lgl,
				   1,
				   "[basicprobe-%d-%d] %d probes remain %.0f%% after last round",
				   lgl->stats->prb.basic.count,
				   round,
				   remprobes,
				   lglpcnt(remprobes, lglrem(lgl)));
		} else if (round >= lgl->opts->prbasicroundlim.val) {
			lglprt(lgl,
				   1,
				   "[basicprobe-%d-%d] round limit %d hit",
				   lgl->stats->prb.basic.count,
				   round,
				   lgl->opts->prbasicroundlim.val);
		} else if (lgl->stats->prb.basic.failed > oldfailed || lgl->stats->prb.basic.lifted > oldlifted ||
				   lgl->stats->hbr.cnt > oldhbr) {
			oldfailed = lgl->stats->prb.basic.failed;
			oldlifted = lgl->stats->prb.basic.lifted;
			lglclnstk(&probes);
			if (oldhbr < lgl->stats->hbr.cnt && lgl->opts->decompose.val)
				if (!lgldecomp(lgl))
					goto DONE;
			oldhbr = lgl->stats->hbr.cnt;
			round++;
			goto RESTART;
		} else {
			assert(!remprobes);
			lglprt(lgl, 1, "[basicprobe-%d-%d] fully completed probing", lgl->stats->prb.basic.count, round);
			for (idx = 2; idx < lgl->nvars; idx++)
				lglavar(lgl, idx)->donotbasicprobe = 0;
		}
	}
DONE:
	lglrelstk(lgl, &lift);
	lglrelstk(lgl, &probes);
	lglrelstk(lgl, &saved);

	assert(lgl->stats->hbr.cnt >= orighbr);
	assert(lglrem(lgl) <= oldrem);
	deltarem = oldrem - lglrem(lgl);
	deltahbr = lgl->stats->hbr.cnt - orighbr;
	success = deltarem || deltahbr;
	LGLUPDPEN(prb.basic, deltarem);
	assert(lgl->stats->prb.basic.failed >= origfailed);
	assert(lgl->stats->prb.basic.lifted >= origlifted);
	failed = lgl->stats->prb.basic.failed - origfailed;
	lifted = lgl->stats->prb.basic.lifted - origlifted;
#ifndef NLGLOG
	assert(lgl->stats->prb.basic.probed >= origprobed);
	probed = lgl->stats->prb.basic.probed - origprobed;
	LOG(1,
		"%ssuccessfully probed %d out of %d probes %.1f%%",
		success ? "" : "un",
		probed,
		nprobes,
		lglpcnt(probed, nprobes));
	LOG(1, "found %d failed %.1f%% lifted %d through probing", failed, lglpcnt(failed, probed), lifted);
#endif
	assert(lgl->probing && lgl->simp && lgl->basicprobing);
	lgl->simp = lgl->probing = lgl->basicprobing = 0;
	units = failed + lifted;
	lglprt(lgl,
		   1 + !units,
		   "[basicprobe-%d-%d] %d units = %d failed (%.0f%%) + %d lifted (%.0f%%)",
		   lgl->stats->prb.basic.count,
		   round,
		   units,
		   failed,
		   lglpcnt(failed, units),
		   lifted,
		   lglpcnt(lifted, units));
	lglprt(lgl,
		   1 + !success,
		   "[basicprobe-%d-%d] removed %d variables, found %d hbrs",
		   lgl->stats->prb.basic.count,
		   round,
		   deltarem,
		   deltahbr);
	lglrep(lgl, 2, 'p');
	lglstop(lgl);
	lglbasicatestats(lgl);
	return !lgl->mt;
}

static int
lglsmallirr(LGL* lgl)
{
	int maxirrlidx = lglcntstk(&lgl->irr), limit;
	int64_t tmp = MAXREDLIDX;
	tmp *= lgl->opts->smallirr.val;
	tmp /= 100;
	limit = (tmp < INT_MAX) ? tmp : INT_MAX;
	if (maxirrlidx >= limit)
		return 0;
	return 1;
}

static int
lglprobe(LGL* lgl)
{
	int res = 1, mod = 0, all, small = lglsmallirr(lgl);
	int (*prb[3])(LGL*);
	lglstart(lgl, &lgl->times->probe);
	if (small && lgl->opts->prbsimple.val)
		prb[mod++] = lglsimpleprobe;
	if (small && lgl->opts->treelook.val)
		prb[mod++] = lgltreelook;
	if (lgl->opts->prbasic.val)
		prb[mod++] = lglbasicprobe;
	all = lgl->stats->prb.simple.count;
	all += lgl->stats->prb.basic.count;
	all += lgl->stats->prb.treelook.count;
	if (lgl->stats->simp.count == 2) {
		int i;
		res = 1;
		for (i = 0; res && i < mod; i++)
			res = prb[i](lgl);
	} else
		res = mod ? prb[all % mod](lgl) : 1;
	lglstop(lgl);
	return res;
}

static void
lglinitevars(LGL* lgl)
{
	EVar* ev;
	int idx;
	assert(lgl->occs);
	NEW(lgl->evars, lgl->nvars);
	for (idx = 2; idx < lgl->nvars; idx++) {
		ev = lgl->evars + idx;
		ev->pos = -1;
	}
}

static void
lgldense(LGL* lgl, int rmredbintrn)
{
	int lit, lidx, count, idx, other, other2, blit, sign, tag, red;
	const int *start, *top, *c, *p, *eow;
	int *q, *w;
	EVar* ev;
	HTS* hts;
	LOG(1, "transition to dense mode");
	assert(!lgl->dense);
	assert(!lgl->rmredbintrn);
	assert(!lgl->evars);
	assert(lglsmallirr(lgl));
	assert(lglmtstk(&lgl->esched));
	assert(lglmtstk(&lgl->saved.bin));
	assert(lglmtstk(&lgl->saved.trn));
	assert(!lgl->eliminating || !lgl->elm->pivot);
	lgl->stats->dense++;
	if (rmredbintrn) {
		lgl->rmredbintrn = 1;
		LOG(1, "temporarily removing redundant binary and ternary clauses");
	}
	count = 0;
	if (lgl->occs)
		lglinitevars(lgl);
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			if (!hts->count)
				continue;
			q = w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				red = blit & REDCS;
				if (red) {
					if (tag == LRGCS)
						continue;
					if (rmredbintrn) {
						assert(tag == BINCS || tag == TRNCS);
						other = blit >> RMSHFT;
						if (abs(other) < idx)
							continue;
						if (tag == TRNCS) {
							other2 = *p;
							if (abs(other2) < idx)
								continue;
							lglpushstk(lgl, &lgl->saved.trn, lit);
							lglpushstk(lgl, &lgl->saved.trn, other);
							lglpushstk(lgl, &lgl->saved.trn, other2);
							assert(lgl->stats->red.trn);
							lgl->stats->red.trn--;
						} else {
							lglpushstk(lgl, &lgl->saved.bin, lit);
							lglpushstk(lgl, &lgl->saved.bin, other);
							assert(lgl->stats->red.bin);
							lgl->stats->red.bin--;
						}
					} else {
						*q++ = blit;
						if (tag == TRNCS)
							*q++ = *p;
					}
				} else {
					*q++ = blit;
					if (tag == LRGCS || tag == TRNCS)
						*q++ = *p;
					if (tag == LRGCS)
						continue;
					assert(tag == BINCS || tag == TRNCS);
					other = blit >> RMSHFT;
					if (abs(other) < idx)
						continue;
					if (tag == TRNCS) {
						other2 = *p;
						if (abs(other2) < idx)
							continue;
						lglincocc(lgl, other2), count++;
					}
					lglincocc(lgl, lit), count++;
					lglincocc(lgl, other), count++;
				}
			}
			lglshrinkhts(lgl, hts, q - w);
		}
	lglfitstk(lgl, &lgl->saved.bin);
	lglfitstk(lgl, &lgl->saved.trn);
	if (count)
		LOG(1, "counted %d occurrences in small irredundant clauses", count);
	{
		count = 0;
		start = lgl->irr.start;
		top = lgl->irr.top;
		for (c = start; c < top; c = p + 1) {
			p = c;
			if (*c >= NOTALIT)
				continue;
			lidx = c - start;
			assert(0 <= lidx), assert(lidx < MAXIRRLIDX);
			blit = (lidx << RMSHFT) | OCCS;
			for (; (lit = *p); p++) {
				hts = lglhts(lgl, lit);
				lglpushwch(lgl, hts, blit);
				lglincocc(lgl, lit), count++;
			}
		}
	}
	if (count)
		LOG(1, "counted %d occurrences in large irredundant clauses", count);
	count = 0;
	if (lgl->occs) {
		for (idx = 2; idx < lgl->nvars; idx++) {
			ev = lglevar(lgl, idx);
			if (ev->pos >= 0)
				continue;
			if (lglifrozen(lgl, idx))
				continue;
			if (lgl->donotsched) {
				AVar* av = lglavar(lgl, idx);
				if (lgl->eliminating && av->donotelm)
					continue;
				if (lgl->blocking && av->donotblk)
					continue;
			}
			assert(!ev->occ[0] && !ev->occ[1]);
			lglesched(lgl, idx);
			count++;
		}
		if (count)
			LOG(1, "scheduled %d zombies", count);
	}
	LOG(1, "continuing in dense mode");
	lgl->dense = 1;
	lglfullyconnected(lgl);
	if (lgl->occs && lgl->opts->verbose.val >= 1) {
		const char* str;
		int inst, vl;
		count = 0;
		if (lgl->eliminating)
			str = "elim", inst = lgl->stats->elm.count, vl = 1;
		else if (lgl->blocking)
			str = "block", inst = lgl->stats->blk.count, vl = 1;
		else
			str = "dense", inst = lgl->stats->dense, vl = 2;
		for (idx = 2; idx < lgl->nvars; idx++)
			if (lglevar(lgl, idx)->pos >= 0)
				count++;
		lglprt(lgl, vl, "[%s-%d] scheduled %d variables %.0f%%", str, inst, count, lglpcnt(count, lgl->nvars - 2));
	}
}

static void
lglsparse(LGL* lgl)
{
	int idx, sign, lit, count, blit, tag;
	int *w, *p, *eow, *q;
	HTS* hts;
	assert(!lgl->notfullyconnected);
	assert(lgl->dense);
	lgl->stats->sparse++;
	count = 0;
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			if (!hts->count)
				continue;
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = q = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == OCCS) {
					count++;
					continue;
				}
				*q++ = blit;
				if (tag == BINCS)
					continue;
				assert(tag == LRGCS || tag == TRNCS);
				assert(p + 1 < eow);
				*q++ = *++p;
			}
			assert(hts->count - (p - q) == q - w);
			lglshrinkhts(lgl, hts, q - w);
		}
	if (lgl->occs) {
		DEL(lgl->evars, lgl->nvars);
		lglrelstk(lgl, &lgl->esched);
	}
	LOG(1, "removed %d full irredundant occurrences", count);
	lgl->dense = 0;
	lgl->notfullyconnected = 1;
	LOG(1, "large clauses not fully connected yet");
}

static int
lglm2i(LGL* lgl, int mlit)
{
	int res, midx = abs(mlit);
	assert(0 < midx);
	res = lglpeek(&lgl->elm->m2i, midx);
	if (mlit < 0)
		res = -res;
	return res;
}

static int
lgli2m(LGL* lgl, int ilit)
{
	AVar* av = lglavar(lgl, ilit);
	int res = av->mark;
	if (!res) {
		res = lglcntstk(&lgl->seen) + 1;
		av->mark = res;
		assert(2 * lglcntstk(&lgl->seen) == lglcntstk(&lgl->elm->noccs) - 2);
		assert(2 * lglcntstk(&lgl->seen) == lglcntstk(&lgl->elm->mark) - 2);
		assert(2 * lglcntstk(&lgl->seen) == lglcntstk(&lgl->elm->occs) - 2);
		assert(lglcntstk(&lgl->seen) == lglcntstk(&lgl->elm->m2i) - 1);
		lglpushstk(lgl, &lgl->seen, abs(ilit));
		lglpushstk(lgl, &lgl->elm->noccs, 0);
		lglpushstk(lgl, &lgl->elm->noccs, 0);
		lglpushstk(lgl, &lgl->elm->mark, 0);
		lglpushstk(lgl, &lgl->elm->mark, 0);
		lglpushstk(lgl, &lgl->elm->occs, 0);
		lglpushstk(lgl, &lgl->elm->occs, 0);
		lglpushstk(lgl, &lgl->elm->m2i, abs(ilit));
		LOG(4, "mapped internal variable %d to marked variable %d", abs(ilit), res);
	}
	if (ilit < 0)
		res = -res;
	return res;
}

static unsigned
lglsig(int lit)
{
	unsigned ulit = lglulit(lit), res;
	assert(ulit >= 2);
	ulit -= 2;
	res = (1u << (ulit & 31));
	return res;
}

static void
lgladdecl(LGL* lgl, const int* c)
{
	int ilit, mlit, umlit, size = 0, next, prev;
	unsigned csig = 0;
	const int* p;
	Val val;
#if !defined(NDEBUG) || !defined(NLGLOG)
	int lidx;
#endif
	LOGCLS(3, c, "copying irredundant clause");
	INCSTEPS(elm.copies);
	size = 0;
	for (p = c; (ilit = *p); p++) {
		val = lglval(lgl, ilit);
		assert(val <= 0);
		if (val < 0)
			continue;
		size++;
		if (abs(ilit) == lgl->elm->pivot)
			continue;
		mlit = lgli2m(lgl, ilit);
		assert(abs(mlit) != 1);
		csig |= lglsig(mlit);
	}
	assert(size >= 1);
	next = lglcntstk(&lgl->elm->lits);
#if !defined(NDEBUG) || !defined(NLGLOG)
	lidx = next;
#endif
	assert(next > 0);
	for (p = c; (ilit = *p); p++) {
		val = lglval(lgl, ilit);
		if (val < 0)
			continue;
		mlit = lgli2m(lgl, ilit);
		lglpushstk(lgl, &lgl->elm->lits, mlit);
		umlit = lglulit(mlit);
		prev = lglpeek(&lgl->elm->occs, umlit);
		lglpushstk(lgl, &lgl->elm->next, prev);
		lglpoke(&lgl->elm->occs, umlit, next++);
		lglpushstk(lgl, &lgl->elm->csigs, csig);
		lglpushstk(lgl, &lgl->elm->sizes, size);
		lgl->elm->noccs.start[umlit]++;
	}
	lglpushstk(lgl, &lgl->elm->lits, 0);
	lglpushstk(lgl, &lgl->elm->next, 0);
	lglpushstk(lgl, &lgl->elm->csigs, 0);
	lglpushstk(lgl, &lgl->elm->sizes, 0);
	lgl->elm->necls++;
	LOGCLS(4, lgl->elm->lits.start + lidx, "copied and mapped clause");
#ifndef NDEBUG
	LOGMCLS(4, lgl->elm->lits.start + lidx, "copied and remapped clause");
	{
		int i, j = 0;
		for (i = 0; c[i]; i++) {
			Val val = lglval(lgl, c[i]);
			assert(val <= 0);
			if (val < 0)
				continue;
			assert(c[i] == lglm2i(lgl, lglpeek(&lgl->elm->lits, lidx + j++)));
		}
	}
#endif
}

static int
lglecls(LGL* lgl, int lit)
{
	int blit, tag, red, other, lidx, count;
	const int *p, *w, *eow, *c;
	int d[4];
	HTS* hts;
	LOG(3, "copying irredundant clauses with %d", lit);
	count = 0;
	hts = lglhts(lgl, lit);
	if (!hts->count)
		return 0;
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == LRGCS)
			continue;
		red = blit & REDCS;
		if (red)
			continue;
		if (tag == BINCS || tag == TRNCS) {
			d[0] = lit;
			other = blit >> RMSHFT;
			d[1] = other;
			if (tag == TRNCS)
				d[2] = *p, d[3] = 0;
			else
				d[2] = 0;
			c = d;
		} else {
			assert(tag == OCCS);
			lidx = (tag == OCCS) ? (blit >> RMSHFT) : *p;
			c = lglidx2lits(lgl, 0, lidx);
		}
		lgladdecl(lgl, c);
		count++;
	}
	return count;
}

static void
lglrstecls(LGL* lgl)
{
	assert(lgl->elm->pivot);
	lglclnstk(&lgl->elm->lits);
	lglclnstk(&lgl->elm->next);
	lglclnstk(&lgl->elm->csigs);
	lglclnstk(&lgl->elm->sizes);
	lglclnstk(&lgl->elm->occs);
	lglclnstk(&lgl->elm->noccs);
	lglclnstk(&lgl->elm->mark);
	lglclnstk(&lgl->elm->m2i);
	lglpopnunmarkstk(lgl, &lgl->seen);
	lgl->elm->pivot = 0;
}

static void
lglrelecls(LGL* lgl)
{
	lglrelstk(lgl, &lgl->elm->lits);
	lglrelstk(lgl, &lgl->elm->next);
	lglrelstk(lgl, &lgl->elm->csigs);
	lglrelstk(lgl, &lgl->elm->sizes);
	lglrelstk(lgl, &lgl->elm->occs);
	lglrelstk(lgl, &lgl->elm->noccs);
	lglrelstk(lgl, &lgl->elm->mark);
	lglrelstk(lgl, &lgl->elm->m2i);
	lglrelstk(lgl, &lgl->elm->clv);
}

static void
lglinitecls(LGL* lgl, int idx)
{
#ifndef NLGLOG
	int clauses;
#endif
	assert(!lgl->elm->pivot);
	assert(idx >= 2);
	assert(lglmtstk(&lgl->elm->lits));
	assert(lglmtstk(&lgl->elm->next));
	assert(lglmtstk(&lgl->elm->csigs));
	assert(lglmtstk(&lgl->elm->sizes));
	assert(lglmtstk(&lgl->elm->occs));
	assert(lglmtstk(&lgl->elm->noccs));
	assert(lglmtstk(&lgl->elm->m2i));
	assert(lglmtstk(&lgl->seen));
	lgl->elm->pivot = idx;
	lglpushstk(lgl, &lgl->elm->mark, 0);
	lglpushstk(lgl, &lgl->elm->mark, 0);
	lglpushstk(lgl, &lgl->elm->occs, 0);
	lglpushstk(lgl, &lgl->elm->occs, 0);
	lglpushstk(lgl, &lgl->elm->noccs, 0);
	lglpushstk(lgl, &lgl->elm->noccs, 0);
	lglpushstk(lgl, &lgl->elm->m2i, 0);
	(void)lgli2m(lgl, idx);
	lglpushstk(lgl, &lgl->elm->lits, 0);
	lglpushstk(lgl, &lgl->elm->next, 0);
	lglpushstk(lgl, &lgl->elm->csigs, 0);
	lglpushstk(lgl, &lgl->elm->sizes, 0);
	lgl->elm->necls = 0;
#ifndef NLGLOG
	clauses =
#endif
		lglecls(lgl, idx);
	lgl->elm->negcls = lgl->elm->necls;
	lgl->elm->neglidx = lglcntstk(&lgl->elm->lits);
#ifndef NLGLOG
	clauses +=
#endif
		lglecls(lgl, -idx);
	LOG(2, "found %d variables in %d clauses with %d or %d", lglcntstk(&lgl->seen), clauses, idx, -idx);
	assert(lgl->elm->pivot);
}

static void
lglelrmcls(LGL* lgl, int lit, int* c, int clidx)
{
	int lidx, i, other, ulit, *lits, *csigs, blit, tag, red, other2;
	int *p, *eow, *w, count;
	HTS* hts;
#ifndef NDEBUG
	int size;
#endif
	lits = lgl->elm->lits.start;
	csigs = lgl->elm->csigs.start;
	assert(lits < c && c < lgl->elm->lits.top - 1);
	lidx = c - lits;
	LOGCLS(2, c, "removing clause");
	for (i = lidx; (other = lits[i]); i++) {
		assert(other < NOTALIT);
		lits[i] = REMOVED;
		csigs[i] = 0;
		ulit = lglulit(other);
		assert(ulit < (int)lglcntstk(&lgl->elm->noccs));
		assert(lgl->elm->noccs.start[ulit] > 0);
		lgl->elm->noccs.start[ulit] -= 1;
	}
#ifndef NDEBUG
	size = lglpeek(&lgl->elm->sizes, lidx);
#endif
	hts = lglhts(lgl, lit);
	assert(hts->count > 0 && (int)hts->count >= clidx);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	blit = tag = count = 0;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == LRGCS)
			continue;
		red = blit & REDCS;
		if (red)
			continue;
		if (count == clidx)
			break;
		count++;
	}
	assert(count == clidx);
	assert(blit && tag);
	assert(p < eow);
	if (tag == BINCS) {
		assert(size >= 2);
		other = blit >> RMSHFT;
		lglrmbcls(lgl, lit, other, 0);
		lgldrupligdelclsarg(lgl, lit, other, 0);
	} else if (tag == TRNCS) {
		other = blit >> RMSHFT;
		other2 = *p;
		lgldrupligdelclsarg(lgl, lit, other, other2, 0);
		lglrmtcls(lgl, lit, other, other2, 0);
	} else {
		assert(tag == OCCS);
		lidx = (tag == OCCS) ? (blit >> RMSHFT) : *p;
#ifndef NDEBUG
		{
			int *q, *d = lglidx2lits(lgl, 0, lidx);
			for (q = d; *q; q++)
				;
			assert(q - d >= size);
		}
#endif
		lgldrupligdelclsaux(lgl, lglidx2lits(lgl, 0, lidx));
		lglrmlcls(lgl, lidx, 0);
	}
}

#define BWL 2

static int
lglbacksub(LGL* lgl, int* c, int str, int** dptr, int* dlidxptr)
{
	int *start = lgl->elm->lits.start, *p, *q, marked = 0, res, *d;
	int lit, ulit, occ, next, osize, other, uolit, size, plit, phase, clidx;
	unsigned ocsig, csig = 0, masksig;
#ifndef NLGLOG
	const char* mode = str ? "strengthening" : "subsumption";
#endif
	LOGMCLS(BWL + 1, c, "backward %s check for clause", mode);
	LOGCLS(BWL + 1, c, "backward %s check for mapped clause", mode);
	phase = (c - start) >= lgl->elm->neglidx;
	for (p = c; (lit = *p); p++)
		if (abs(lit) != 1)
			csig |= lglsig(lit);
	size = p - c;
	assert(csig == (unsigned)lglpeek(&lgl->elm->csigs, c - start));
	assert(size == lglpeek(&lgl->elm->sizes, c - start));
	res = 0;
	if (dptr)
		*dptr = 0;
	if (dlidxptr)
		*dlidxptr = 0;

	if (str)
		phase = !phase;
	lit = phase ? -1 : 1;
	masksig = ~(lglsig(-lit) | lglsig(lit));

	ulit = lglulit(lit);
	occ = lglpeek(&lgl->elm->noccs, ulit);
	if (!str && occ <= 1)
		return 0;
	if (str && !occ)
		return 0;
	if (occ > lgl->elm->bkwdocclim)
		return 0;
	for (next = lglpeek(&lgl->elm->occs, ulit); !res && next && lgl->limits->elm.steps > lgl->stats->elm.steps;
		 next = lglpeek(&lgl->elm->next, next)) {
		INCSTEPS(elm.steps);
		if (next == p - start)
			continue;
		if (phase != (next >= lgl->elm->neglidx))
			continue;
		plit = lglpeek(&lgl->elm->lits, next);
		if (plit >= NOTALIT)
			continue;
		assert(plit == lit);
		osize = lglpeek(&lgl->elm->sizes, next);
		INCSTEPS(elm.steps);
		if (osize > size)
			continue;
		ocsig = lglpeek(&lgl->elm->csigs, next);
		INCSTEPS(elm.steps);
		assert(ocsig);
		ocsig &= masksig;
		if ((ocsig & ~csig))
			continue;
		if (!marked) {
			for (q = c; (other = *q); q++) {
				if (str && abs(other) == 1)
					other = -other;
				uolit = lglulit(other);
				assert(!lglpeek(&lgl->elm->mark, uolit));
				lglpoke(&lgl->elm->mark, uolit, 1);
			}
			marked = 1;
			ADDSTEPS(elm.steps, q - c);
		}
		d = lgl->elm->lits.start + next;
		if (c <= d && d < c + size)
			continue;
		if (str)
			lgl->stats->elm.strchks++;
		else
			lgl->stats->elm.subchks++;
		while (d[-1])
			d--;
		assert(c != d);
		LOGMCLS(BWL + 1, d, "backward %s check with clause", mode);
		res = 1;
		for (q = d; res && (other = *q); q++) {
			uolit = lglulit(other);
			res = lglpeek(&lgl->elm->mark, uolit);
		}
		ADDSTEPS(elm.steps, q - d);
		if (!res || !str || osize < size)
			continue;
		ADDSTEPS(elm.steps, q - d);
		LOGMCLS(BWL, d, "static double strengthened by double self-subsuming resolution");
		q = lgl->elm->lits.start;
		if (phase) {
			assert((c - start) < lgl->elm->neglidx);
			assert((d - start) >= lgl->elm->neglidx);
			q += lgl->elm->neglidx;
		} else {
			assert(!phase);
			assert((d - start) < lgl->elm->neglidx);
			assert((c - start) >= lgl->elm->neglidx);
			assert(!lglpeek(&lgl->elm->lits, 0));
			q++;
		}
		clidx = 0;
		while (q < d) {
			other = *q++;
			if (other >= NOTALIT) {
				while (*q++)
					;
				continue;
			}
			if (!other)
				clidx++;
		}
		LOGMCLS(BWL, d, "strengthened and subsumed original irredundant clause");
		LOGCLS(BWL, d, "strengthened and subsumed mapped irredundant clause");
		*dptr = d, *dlidxptr = clidx;
		assert(res);
	}
	if (marked) {
		for (p = c; (lit = *p); p++) {
			if (str && abs(lit) == 1)
				lit = -lit;
			ulit = lglulit(lit);
			assert(lglpeek(&lgl->elm->mark, ulit));
			lglpoke(&lgl->elm->mark, ulit, 0);
		}
		ADDSTEPS(elm.steps, p - c);
	}
	return res;
}

static void
lglelmsub(LGL* lgl)
{
	int clidx, count, subsumed, pivot, *c;
	if (!lgl->opts->elmotfsub.val)
		return;
	count = clidx = subsumed = 0;
	pivot = lgl->elm->pivot;
	for (c = lgl->elm->lits.start + 1; c < lgl->elm->lits.top && lgl->limits->elm.steps > lgl->stats->elm.steps; c++) {
		INCSTEPS(elm.steps);
		if (count++ == lgl->elm->negcls)
			clidx = 0, pivot = -pivot;
		if (lglbacksub(lgl, c, 0, 0, 0)) {
			subsumed++;
			lgl->stats->elm.sub++;
			LOGMCLS(BWL, c, "subsumed original irredundant clause");
			LOGCLS(3, c, "subsumed mapped irredundant clause");
			lglelrmcls(lgl, pivot, c, clidx);
		} else
			clidx++;
		while (*c)
			c++;
	}
	LOG(BWL + !subsumed, "subsumed %d clauses containing %d or %d", subsumed, lgl->elm->pivot, -lgl->elm->pivot);
}

static int
lglelmstr(LGL* lgl)
{
	int clidx, count, strengthened, pivot, *c, *p, mlit, ilit, res, found;
	int *d, dlidx;
	int size;
	if (!lgl->opts->elmotfstr.val)
		return 0;
	count = clidx = strengthened = 0;
	pivot = lgl->elm->pivot;
	res = 0;
	LOG(3, "strengthening with pivot %d", pivot);
	for (c = lgl->elm->lits.start + 1; c < lgl->elm->lits.top && lgl->limits->elm.steps > lgl->stats->elm.steps; c++) {
		INCSTEPS(elm.steps);
		if (count++ == lgl->elm->negcls) {
			clidx = 0, pivot = -pivot;
			LOG(3, "strengthening with pivot %d", pivot);
		}
		if (*c == REMOVED) {
			while (*c) {
				assert(*c == REMOVED);
				c++;
			}
			continue;
		}
		if (lglbacksub(lgl, c, 1, &d, &dlidx)) {
			strengthened++;
			lgl->stats->elm.str++;
			LOGMCLS(2, c, "strengthening original irredundant clause");
			LOGCLS(3, c, "strengthening mapped irredundant clause");
			assert(lglmtstk(&lgl->clause));
			found = 0;
			size = 0;
			for (p = c; (mlit = *p); p++) {
				ilit = lglm2i(lgl, *p);
				if (ilit == pivot) {
					found++;
					continue;
				}
				assert(!lglval(lgl, ilit));
				lglpushstk(lgl, &lgl->clause, ilit);
				size++;
			}
			assert(found);
			lglpushstk(lgl, &lgl->clause, 0);
			LOGCLS(2, lgl->clause.start, "static strengthened irredundant clause");
			lgldrupligaddcls(lgl, REDCS);
			if (d)
				lglelrmcls(lgl, -pivot, d, dlidx);
			lglelrmcls(lgl, pivot, c, clidx);
			lgladdcls(lgl, 0, 0, 1);
			lglclnstk(&lgl->clause);
			if (size == 1) {
				res = 1;
				break;
			}
		} else
			clidx++;
		while (*c)
			c++;
	}
	LOG(2, "strengthened %d clauses containing %d or %d", strengthened, lgl->elm->pivot, -lgl->elm->pivot);
	return res;
}

static int
lglflushclauses(LGL* lgl, int lit)
{
	int blit, tag, red, other, other2, count, glue, res;
	const int druplig = lgl->opts->druplig.val;
	const int *p, *w, *eow;
	int lidx, glidx, slidx;
	int *c, *q;
	HTS* hts;
	Stk* s;
#ifndef NDEBUG
	int occs;
#endif
	lglchkirrstats(lgl);
	assert(lgl->probing || lgl->lkhd || lgl->dense);
	hts = lglhts(lgl, lit);
	if (!hts->count)
		return 0;
#ifndef NDEBUG
	occs = lgl->occs ? lglocc(lgl, lit) : 0;
#endif
	res = 0;
	LOG(2, "flushing clauses with literal %d", lit);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	count = 0;
	for (p = w; p < eow; p++) {
		if (lgl->blocking)
			INCSTEPS(blk.steps);
		if (lgl->eliminating)
			INCSTEPS(elm.steps);
		if (lgl->simpleprobing)
			INCSTEPS(prb.simple.steps);
		blit = *p;
		tag = blit & MASKCS;
		red = blit & REDCS;
		other = blit >> RMSHFT;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == BINCS) {
			lglrmbwch(lgl, other, lit, red);
			LOG(2, "flushed %s binary clause %d %d", lglred2str(red), lit, other);
			if (druplig)
				lgldrupligdelclsarg(lgl, lit, other, 0);
			lgldeclscnt(lgl, 2, red, 0);
			if (!red)
				lgldecocc(lgl, lit), lgldecocc(lgl, other), res++;
			count++;
		} else if (tag == TRNCS) {
			other2 = *p;
			lglrmtwch(lgl, other2, lit, other, red);
			lglrmtwch(lgl, other, lit, other2, red);
			LOG(2, "flushed %s ternary clause %d %d %d", lglred2str(red), lit, other, other2);
			if (druplig)
				lgldrupligdelclsarg(lgl, lit, other, other2, 0);
			lgldeclscnt(lgl, 3, red, 0);
			if (!red) {
				lgldecocc(lgl, lit);
				lgldecocc(lgl, other);
				lgldecocc(lgl, other2);
				res++;
			}
			count++;
		} else {
			assert(tag == OCCS || tag == LRGCS);
			if (tag == LRGCS) {
				lidx = *p;
				s = lglidx2stk(lgl, red, lidx);
				if (red) {
					glue = lidx & GLUEMASK;
					c = s->start + (lidx >> GLUESHFT);
				} else {
					glue = 0;
					c = s->start + lidx;
				}
			} else {
				lidx = (blit >> RMSHFT);
				s = &lgl->irr;
				c = s->start + lidx;
				glue = 0;
			}
			if (c >= s->top || (other = c[0]) >= NOTALIT)
				continue;
			LOGCLS(2, c, "flushed %s large clause", lglred2str(red));
			if (druplig)
				lgldrupligdelclsaux(lgl, c);
			if (tag == LRGCS) {
				if (other == lit)
					other = c[1];
				assert(abs(other) != abs(lit));
				lglrmlwch(lgl, other, red, lidx);
			} else {
				glidx = lidx;
				if (red)
					glidx <<= GLUESHFT;
				if (c[1] != lit)
					lglrmlwch(lgl, c[1], red, glidx);
				if (other != lit)
					lglrmlwch(lgl, other, red, glidx);
			}
			if (red) {
				assert(!glue);
				LGLCHKACT(c[-1]);
				c[-1] = REMOVED;
			} else
				lgldecocc(lgl, lit);
			for (q = c; (other = *q); q++) {
				assert(other < NOTALIT);
				*q = REMOVED;
				if (other == lit)
					continue;
				if (red && glue)
					continue;
				slidx = lidx;
				if (red && tag == LRGCS)
					slidx >>= GLUESHFT;
				lglrmlocc(lgl, other, red, slidx);
				if (!red)
					lgldecocc(lgl, other);
			}
			*q = REMOVED;
			lgldeclscnt(lgl, q - c, red, glue);
			lgltrimlitstk(lgl, red, lidx);
			if (!red)
				res++;
			count++;
		}
	}
#ifndef NDEBUG
	if (lgl->occs)
		assert(occs == res);
#endif
	lglshrinkhts(lgl, hts, 0);
	LOG(2, "flushed %d clauses with %d including %d irredundant", count, lit, res);
	lglchkirrstats(lgl);
	return res;
}

static int
lglflushlits(LGL* lgl, int lit)
{
	int blit, tag, red, other, other2, size, satisfied, d[3], glue;
	const int druplig = lgl->opts->druplig.val;
	int *p, *w, *eow, *c, *l, *k;
	int lidx, slidx, glidx;
	int count, res;
	Val val, val2;
	ptrdiff_t delta;
	Stk saved;
	Stk* s;

#define FIXPTRS()                                                                                                      \
	do {                                                                                                               \
		p += delta, w += delta, eow += delta;                                                                          \
	} while (0)

	HTS* hts;
	LOG(2, "flushing literal %d from clauses", lit);
	assert(!lgl->level);
	assert(lglifixed(lgl, lit) < 0);
	assert(lgl->dense);
	lglchkirrstats(lgl);
	hts = lglhts(lgl, lit);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	res = count = 0;
	CLR(saved);
	for (p = w; p < eow; p++) {
		if (lgl->blocking)
			INCSTEPS(blk.steps);
		if (lgl->eliminating)
			INCSTEPS(elm.steps);
		if (lgl->simpleprobing)
			INCSTEPS(prb.simple.steps);
		count++;
		blit = *p;
		tag = blit & MASKCS;
		red = blit & REDCS;
		if (tag == BINCS) {
			other = blit >> RMSHFT;
			assert((red && lgliselim(lgl, other)) || lglval(lgl, other) > 0);
			lglrmbwch(lgl, other, lit, red);
			LOG(2, "flushed %s binary clause %d %d", lglred2str(red), lit, other);
			if (druplig)
				lgldrupligdelclsarg(lgl, lit, other, 0);
			lgldeclscnt(lgl, 2, red, 0);
			if (!red) {
				if (lgl->dense)
					lgldecocc(lgl, lit), lgldecocc(lgl, other);
				res++;
			}
		} else if (tag == TRNCS) {
			other = blit >> RMSHFT;
			other2 = *++p;
			lglrmtwch(lgl, other2, lit, other, red);
			lglrmtwch(lgl, other, lit, other2, red);
			LOG(2, "flushed %s ternary clause %d %d %d", lglred2str(red), lit, other, other2);
			lgldeclscnt(lgl, 3, red, 0);
			if (!red) {
				if (lgl->dense) {
					lgldecocc(lgl, lit);
					lgldecocc(lgl, other);
					lgldecocc(lgl, other2);
				}
				res++;
			}
			val = lglval(lgl, other);
			val2 = lglval(lgl, other2);
			if (!val && !val2) {
				LOG(2,
					"reducing flushed %s ternary clause %d %d %d to binary %s clause %d %d",
					lglred2str(red),
					lit,
					other,
					other2,
					lglred2str(red),
					other,
					other2);
				delta = lglwchbin(lgl, other, other2, red);
				delta += lglwchbin(lgl, other2, other, red);
				if (lgl->simpleprobing && lgl->opts->prbsimple.val >= 2)
					lglwrktouch(lgl, -other), lglwrktouch(lgl, -other2);
				if (delta)
					FIXPTRS();
				if (red) {
					lgl->stats->red.bin++, assert(lgl->stats->red.bin > 0);
				} else {
					lglincirr(lgl, 2);
					if (lgl->dense)
						lglincocc(lgl, other), lglincocc(lgl, other2);
				}
				if (druplig)
					lgldrupligaddclsarg(lgl, REDCS, other, other2, 0);
			} else {
#ifndef NDEBUG
				if (!red || (!lgliselim(lgl, other) && !lgliselim(lgl, other2)))
					assert(val > 0 || val2 > 0);
#endif
			}
			if (druplig)
				lgldrupligdelclsarg(lgl, lit, other, other2, 0);
		} else {
			assert(tag == OCCS || tag == LRGCS);
			lidx = (tag == LRGCS) ? *++p : (blit >> RMSHFT);
			s = lglidx2stk(lgl, red, lidx);
			c = s->start + (red ? (lidx >> GLUESHFT) : lidx);
			if (c >= s->top || c[0] >= NOTALIT)
				continue;
			size = satisfied = 0;
			assert(lglmtstk(&saved));
			for (l = c; (other = *l); l++) {
				if (druplig)
					lglpushstk(lgl, &saved, other);
				if (satisfied)
					continue;
				if (other == lit)
					continue;
				if ((val = lglval(lgl, other)) < 0)
					continue;
				if (val > 0) {
					satisfied = 1;
					continue;
				}
				if (size < 3)
					d[size] = other;
				size++;
			}
			if (!satisfied && size == 2) {
				LOGCLS(2,
					   c,
					   "reducing to binary %s clause %d %d flushed large %s clause",
					   lglred2str(red),
					   d[0],
					   d[1],
					   lglred2str(red));
				delta = lglwchbin(lgl, d[0], d[1], red);
				delta += lglwchbin(lgl, d[1], d[0], red);
				if (delta)
					FIXPTRS();
				if (red) {
					lgl->stats->red.bin++, assert(lgl->stats->red.bin > 0);
				} else {
					lglincirr(lgl, 2);
					if (lgl->dense)
						lglincocc(lgl, d[0]), lglincocc(lgl, d[1]);
				}
				if (lgl->simpleprobing && lgl->opts->prbsimple.val >= 2)
					lglwrktouch(lgl, -d[0]), lglwrktouch(lgl, -d[1]);
				if (druplig)
					lgldrupligaddclsarg(lgl, REDCS, d[0], d[1], 0);
			}
			if (!satisfied && size == 3) {
				LOGCLS(2,
					   c,
					   "reducing to ternary %s clause %d %d %d flushed large %s clause",
					   lglred2str(red),
					   d[0],
					   d[1],
					   d[2],
					   lglred2str(red));
				delta = lglwchtrn(lgl, d[0], d[1], d[2], red);
				delta += lglwchtrn(lgl, d[1], d[0], d[2], red);
				delta += lglwchtrn(lgl, d[2], d[0], d[1], red);
				if (delta)
					FIXPTRS();
				if (red) {
					lgl->stats->red.trn++, assert(lgl->stats->red.trn > 0);
				} else {
					lglincirr(lgl, 3);
					if (lgl->dense) {
						lglincocc(lgl, d[0]);
						lglincocc(lgl, d[1]);
						lglincocc(lgl, d[2]);
					}
				}
				if (lgl->simpleprobing && lgl->opts->prbsimple.val >= 3)
					lglwrktouch(lgl, d[0]), lglwrktouch(lgl, d[1]), lglwrktouch(lgl, d[2]);
				if (druplig)
					lgldrupligaddclsarg(lgl, REDCS, d[0], d[1], d[2], 0);
			}
			if (lgl->dense && !red) {
				for (l = c; (other = *l); l++) {
					if (satisfied || size <= 3 || lglval(lgl, other) < 0) {
						if (!red)
							lgldecocc(lgl, other);
						if (other != lit) {
							slidx = lidx;
							if (red && tag == LRGCS)
								slidx >>= GLUESHFT;
							lglrmlocc(lgl, other, red, slidx);
						}
					}
				}
			}
			glidx = lidx;
			if (red && tag == OCCS)
				glidx <<= GLUESHFT;
			if (c[0] != lit)
				lglrmlwch(lgl, c[0], red, glidx);
			if (c[1] != lit)
				lglrmlwch(lgl, c[1], red, glidx);
			if (satisfied || size <= 3) {
				if (red) {
					LGLCHKACT(c[-1]);
					c[-1] = REMOVED;
				}
				for (k = c; (other = *k); k++)
					*k = REMOVED;
				*k = REMOVED;
				if (red) {
					glue = (tag == LRGCS) ? (lidx & GLUEMASK) : 0;
					assert(lgl->stats->lir[glue].clauses > 0);
					lgl->stats->lir[glue].clauses--;
					assert(lgl->stats->red.lrg > 0);
					lgl->stats->red.lrg--;
				} else
					lgldecirr(lgl, k - c);
			} else {
				for (l = k = c; (other = *l); l++) {
					if ((val = lglval(lgl, other)) < 0)
						continue;
					if (lgl->simpleprobing && lgl->opts->prbsimple.val >= 3)
						lglwrktouch(lgl, other);
					assert(abs(other) != abs(lit));
					assert(!val);
					*k++ = other;
				}
				assert(size == k - c);
				if (!red && k < l) {
					assert(lgl->stats->irr.lits.cur >= l - k);
					lgl->stats->irr.lits.cur -= l - k;
				}
				*k++ = 0;
				while (k <= l)
					*k++ = REMOVED;
				delta = lglwchlrg(lgl, c[0], c[1], red, glidx);
				delta += lglwchlrg(lgl, c[1], c[0], red, glidx);
				if (delta)
					FIXPTRS();
				if (druplig)
					lgldrupligaddclsaux(lgl, REDCS, c);
			}
			lgltrimlitstk(lgl, red, lidx);
			if (druplig) {
				assert(lglcntstk(&saved) >= 4);
				lglpushstk(lgl, &saved, 0);
				lgldrupligdelclsaux(lgl, saved.start);
				lglclnstk(&saved);
			}
		}
	}
	lglrelstk(lgl, &saved);
	hts = lglhts(lgl, lit);
	lglshrinkhts(lgl, hts, 0);
	LOG(2, "flushed %d occurrences of literal %d including %d irredundant", count, lit, res);
	lglchkirrstats(lgl);
	return res;

#undef FIXPTRS
}

static int
lglflushed(LGL* lgl)
{
	assert(lgl->flushed <= (int)lglcntstk(&lgl->trail));
	return lgl->flushed == (int)lglcntstk(&lgl->trail);
}

static int
lglflush(LGL* lgl)
{
	int lit, count;
	if (lgl->mt)
		return 0;
	lglchkirrstats(lgl);
	assert(!lgl->level);
	assert(lgl->probing || lgl->lkhd || lgl->dense);
	if (lglflushed(lgl))
		return 1;
	if (!lglbcp(lgl)) {
		lglmt(lgl);
		return 0;
	}
	if (!lglsyncunits(lgl)) {
		assert(lgl->mt);
		return 0;
	}
	count = 0;
	while (lgl->flushed < (int)lglcntstk(&lgl->trail)) {
		lit = lglpeek(&lgl->trail, lgl->flushed++);
		lglflushclauses(lgl, lit);
		lglflushlits(lgl, -lit);
		count++;
	}
	LOG(2, "flushed %d literals", count);
	assert(!lgl->mt);
	return 1;
}

static void
lglblockinglit(LGL* lgl, int ilit)
{
	int elit = lglexport(lgl, ilit), sgnbit = (1 << (elit < 0));
	Ext* ext = lglelit2ext(lgl, elit);
	assert(!ext->equiv);
	assert(!ext->eliminated);
	assert(abs(ext->repr) == abs(ilit));
	if (ext->blocking & sgnbit)
		return;
	ext->blocking |= sgnbit;
	LOG(3, "marking external %d internal %d as blocking", elit, ilit);
	lgl->stats->blk.lits++;
}

static void
lglelmfrelit(LGL* lgl, int mpivot, int* sop, int* eop, int* son, int* eon)
{
	int ipivot = mpivot * lgl->elm->pivot, clidx, ilit, tmp, cover, maxcover;
	int *c, *d, *p, *q, lit, nontrivial, idx, sgn, clen, reslen;
	assert(mpivot == 1 || mpivot == -1);
	assert(ipivot);
	LOG(3, "blocked clause elimination and forced resolution of clauses with %d", ipivot);
	clidx = 0;
	cover = lglpeek(&lgl->elm->noccs, lglulit(-mpivot));
	for (c = sop; c < eop; c = p + 1) {
		if (lgl->eliminating)
			INCSTEPS(elm.steps);
		if (*c == REMOVED) {
			for (p = c + 1; *p; p++)
				;
			continue;
		}
		maxcover = 0;
		for (p = c; (lit = *p); p++) {
			if (lit == mpivot)
				continue;
			assert(lit != -mpivot);
			maxcover += lglpeek(&lgl->elm->noccs, lglulit(-lit));
		}
		if (maxcover < cover - 1) {
			clidx++;
			continue;
		}
		for (p = c; (lit = *p); p++) {
			if (lit == mpivot)
				continue;
			assert(lit != -mpivot);
			idx = abs(lit);
			assert(!lglpeek(&lgl->elm->mark, idx));
			sgn = lglsgn(lit);
			lglpoke(&lgl->elm->mark, idx, sgn);
		}
		nontrivial = 0;
		clen = p - c;
		for (d = son; !nontrivial && d < eon; d = q + 1) {
			INCSTEPS(elm.steps);
			if (*d == REMOVED) {
				for (q = d + 1; *q; q++)
					;
				continue;
			}
			INCSTEPS(elm.resolutions);
			LOGMCLS(3, c, "trying forced resolution 1st antecedent");
			LOGMCLS(3, d, "trying forced resolution 2nd antecedent");
			assert(clen > 0);
			reslen = clen - 1;
			for (q = d; (lit = *q); q++) {
				if (lit == -mpivot)
					continue;
				assert(lit != mpivot);
				idx = abs(lit), sgn = lglsgn(lit);
				tmp = lglpeek(&lgl->elm->mark, idx);
				if (tmp == -sgn)
					break;
				if (tmp != sgn)
					reslen++;
			}
			if (lit) {
				while (*++q)
					;
				LOG(3, "trying forced resolution ends with trivial resolvent");
			} else {
				LOG(3, "non trivial resolvent in blocked clause elimination");
				nontrivial = INT_MAX;
			}
		}
		for (p = c; (lit = *p); p++) {
			if (lit == mpivot)
				continue;
			assert(lit != -mpivot);
			idx = abs(lit);
			assert(lglpeek(&lgl->elm->mark, idx) == lglsgn(lit));
			lglpoke(&lgl->elm->mark, idx, 0);
		}
		assert(lgl->opts->elim.val);
		if (!nontrivial && lgl->opts->block.val && lgl->opts->elmblk.val &&
			(!lgl->opts->elmblkwait.val || !lgl->opts->elim.val || lgl->elmrtc)) {
			assert(maxcover >= cover);
			lgl->stats->elm.blkd++;
			LOGMCLS(2, c, "blocked on %d clause", ipivot);
			lglepush(lgl, ipivot);
			for (p = c; (lit = *p); p++) {
				if (lit == mpivot)
					continue;
				assert(lit != -mpivot);
				ilit = lglm2i(lgl, lit);
				lglepush(lgl, ilit);
			}
			lglepush(lgl, 0);
			lglelrmcls(lgl, ipivot, c, clidx);
			lglblockinglit(lgl, ipivot);
			continue;
		}
		clidx++;
		if (lgl->limits->elm.steps <= lgl->stats->elm.steps) {
			LOG(2, "maximum number of steps in elimination exhausted");
			return;
		}
	}
}

static void
lglelmfre(LGL* lgl)
{
	int *sop, *eop, *son, *eon;
	assert(lgl->elm->pivot);
	sop = lgl->elm->lits.start + 1;
	eop = son = lgl->elm->lits.start + lgl->elm->neglidx;
	eon = lgl->elm->lits.top;
	lglelmfrelit(lgl, 1, sop, eop, son, eon);
	lglelmfrelit(lgl, -1, son, eon, sop, eop);
}

static void
lgleliminated(LGL* lgl, int pivot)
{
	AVar* av;
	int elit;
	Ext* e;
	assert(!lglifrozen(lgl, pivot));
	av = lglavar(lgl, pivot);
	assert(av->type == FREEVAR);
	av->type = ELIMVAR;
	lgl->stats->elm.elmd++;
	assert(lgl->stats->elm.elmd > 0);
	lglflushclauses(lgl, pivot);
	lglflushclauses(lgl, -pivot);
	LOG(2, "eliminated %d", pivot);
	elit = lglexport(lgl, pivot);
	e = lglelit2ext(lgl, elit);
	assert(!e->eliminated);
	assert(!e->equiv);
	e->eliminated = 1;
}

static void
lglepusheliminated(LGL* lgl, int idx)
{
	const int *p, *w, *eow, *c, *l;
	int lit, blit, tag, red, other;
	HTS* hts;
	lit = (lglocc(lgl, idx) < lglocc(lgl, -idx)) ? idx : -idx;
	LOG(3, "keeping clauses with %d for extending assignment", lit);
	hts = lglhts(lgl, lit);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == LRGCS)
			continue;
		red = blit & REDCS;
		if (red)
			continue;
		lglepush(lgl, lit);
		if (tag == BINCS || tag == TRNCS) {
			lglepush(lgl, blit >> RMSHFT);
			if (tag == TRNCS)
				lglepush(lgl, *p);
		} else {
			assert(tag == OCCS);
			c = lglidx2lits(lgl, 0, blit >> RMSHFT);
			for (l = c; (other = *l); l++)
				if (other != lit)
					lglepush(lgl, other);
		}
		lglepush(lgl, 0);
	}
	lglepush(lgl, -lit);
	lglepush(lgl, 0);
	lgleliminated(lgl, idx);
}

static int
lglunhimpl(const DFPR* dfpr, int a, int b)
{
	int u = lglulit(a), v = lglulit(b), c, d, f, g;
	c = dfpr[u].discovered;
	if (!c)
		return 0;
	d = dfpr[v].discovered;
	if (!d)
		return 0;
	f = dfpr[u].finished, g = dfpr[v].finished;
	assert(0 < c && c < f);
	assert(0 < d && d < g);
	return c < d && g < f;
}

static int
lglunhimplies2(const DFPR* dfpr, int a, int b)
{
	return lglunhimpl(dfpr, a, b) || lglunhimpl(dfpr, -b, -a);
}

static int
lglunhimplincl(const DFPR* dfpr, int a, int b)
{
	int u = lglulit(a), v = lglulit(b), c, d, f, g;
	c = dfpr[u].discovered;
	if (!c)
		return 0;
	d = dfpr[v].discovered;
	if (!d)
		return 0;
	f = dfpr[u].finished, g = dfpr[v].finished;
	assert(0 < c && c < f);
	assert(0 < d && d < g);
	return c <= d && g <= f;
}

static int
lglunhimplies2incl(const DFPR* dfpr, int a, int b)
{
	return lglunhimplincl(dfpr, a, b) || lglunhimplincl(dfpr, -b, -a);
}

static int
lglhastrn(LGL* lgl, int a, int b, int c)
{
	int blit, tag, other, other2;
	const int *w, *eow, *p;
	HTS *ha, *hb, *hc;
	ha = lglhts(lgl, a);
	hb = lglhts(lgl, b);
	if (hb->count < ha->count) {
		SWAP(int, a, b);
		SWAP(HTS*, ha, hb);
	}
	hc = lglhts(lgl, c);
	if (hc->count < ha->count) {
		SWAP(int, a, c);
		SWAP(HTS*, ha, hc);
	}
	w = lglhts2wchs(lgl, ha);
	eow = w + ha->count;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == OCCS)
			continue;
		if (tag == BINCS) {
			other = blit >> RMSHFT;
			if (other == b || other == c)
				return 1;
			continue;
		}
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == LRGCS)
			continue;
		assert(tag == TRNCS);
		other = blit >> RMSHFT;
		if (other != b && other != c)
			continue;
		other2 = *p;
		if (other2 == b || other2 == c)
			return 1;
	}
	if (hc->count < hb->count) {
		SWAP(int, b, c);
		SWAP(HTS*, hb, hc);
	}
	w = lglhts2wchs(lgl, hb);
	eow = w + hb->count;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == OCCS)
			continue;
		if (tag == TRNCS || tag == LRGCS) {
			p++;
			continue;
		}
		assert(tag == BINCS);
		other = blit >> RMSHFT;
		if (other == c)
			return 1;
	}
	return 0;
}

static int64_t
lglfactor(LGL* lgl, int lim, int count)
{
	int64_t factor, res;
	if (!count)
		return lim;
	switch (lgl->opts->factor.val) {
		default:
			factor = 1;
			break;
		case 1:
			factor = lglceild(count);
			break;
		case 2:
			factor = count;
			break;
		case 3:
			factor = count * (int64_t)count;
			break;
	}
	if (!factor)
		factor = 1;
	if (factor > lgl->opts->factmax.val)
		factor = lgl->opts->factmax.val;
	res = lim * factor;
	return res;
}

static int
lglforcedve(LGL* lgl, int idx)
{
	int pocc = lglocc(lgl, idx);
	int nocc = lglocc(lgl, -idx);
	int count = lgl->stats->elm.count;
	if (!pocc)
		return 1;
	if (!nocc)
		return 1;
	if (pocc + nocc <= lgl->opts->elmoccsumforced.val)
		return 1;
	if (lgl->opts->elmfull.val)
		return 0;
	if (pocc >= lglfactor(lgl, lgl->opts->elmocclim1.val, count))
		return -1;
	if (nocc >= lglfactor(lgl, lgl->opts->elmocclim1.val, count))
		return -1;
	if (pocc < lglfactor(lgl, lgl->opts->elmocclim2.val, count))
		return 0;
	if (nocc < lglfactor(lgl, lgl->opts->elmocclim2.val, count))
		return 0;
	return -1;
}

static int
lgltrylargeve(LGL* lgl)
{
	const int *c, *d, *sop, *eop, *son, *eon, *p, *q, *start, *end;
	int lit, idx, sgn, tmp, ip, ilit, npocc, nnocc, limit, count, i, res;
	int clen, dlen, reslen, maxreslen;
	ip = lgl->elm->pivot;
	assert(ip);
	if ((res = lglforcedve(lgl, ip)))
		return res > 0;
	sop = lgl->elm->lits.start + 1;
	eop = son = lgl->elm->lits.start + lgl->elm->neglidx;
	eon = lgl->elm->lits.top;
	npocc = lglpeek(&lgl->elm->noccs, lglulit(1));
	nnocc = lglpeek(&lgl->elm->noccs, lglulit(-1));
	limit = npocc + nnocc;
	count = 0;
	for (i = 0; i <= 1; i++) {
		start = i ? son : sop;
		end = i ? eon : eop;
		for (c = start; c < end; c++) {
			INCSTEPS(elm.steps);
			if (*c == REMOVED) {
				while (*c)
					c++;
				continue;
			}
			while ((lit = *c)) {
				(void)lglm2i(lgl, lit);
				c++;
			}
			count++;
		}
	}
	assert(count == limit);
	LOG(3, "trying clause distribution for %d with limit %d", ip, limit);
	maxreslen = 0;
	for (c = sop; c < eop && limit >= 0; c = p + 1) {
		INCSTEPS(elm.steps);
		if (*c == REMOVED) {
			for (p = c + 1; *p; p++)
				;
			continue;
		}
		assert(lglmtstk(&lgl->resolvent));
		clen = 0;
		for (p = c; (lit = *p); p++) {
			if (lit == 1)
				continue;
			assert(lit != -1);
			idx = abs(lit);
			assert(!lglpeek(&lgl->elm->mark, idx));
			sgn = lglsgn(lit);
			lglpoke(&lgl->elm->mark, idx, sgn);
			ilit = lglm2i(lgl, lit);
			lglpushstk(lgl, &lgl->resolvent, ilit);
			clen++;
		}
		for (d = son; limit >= 0 && d < eon; d = q + 1) {
			INCSTEPS(elm.steps);
			if (*d == REMOVED) {
				for (q = d + 1; *q; q++)
					;
				continue;
			}
			INCSTEPS(elm.resolutions);
			LOGMCLS(3, c, "trying resolution 1st antecedent");
			LOGMCLS(3, d, "trying resolution 2nd antecedent");
			dlen = 0;
			reslen = clen;
			for (q = d; (lit = *q); q++) {
				if (lit == -1)
					continue;
				dlen++;
				assert(lit != 1);
				idx = abs(lit), sgn = lglsgn(lit);
				tmp = lglpeek(&lgl->elm->mark, idx);
				if (tmp == -sgn)
					break;
				if (tmp == sgn)
					continue;
				ilit = lglm2i(lgl, lit);
				lglpushstk(lgl, &lgl->resolvent, ilit);
				reslen++;
			}
			assert(reslen == (int)lglcntstk(&lgl->resolvent));
			if (!lit && reslen == 1) {
				LOG(3, "trying resolution ends with unit clause");
				lit = lglpeek(&lgl->resolvent, 0);
				limit += lglevar(lgl, lit)->occ[lit < 0];
			} else if (lit) {
				while (*++q)
					;
				LOG(3, "trying resolution ends with trivial resolvent");
			} else {
				limit--;
				LOG(3, "trying resolution with non trivial resolvent remaining %d", limit);
				if (reslen > maxreslen)
					maxreslen = reslen;
			}
			assert(!*q);
			lglrststk(&lgl->resolvent, clen);
		}
		lglclnstk(&lgl->resolvent);
		for (p = c; (lit = *p); p++) {
			if (lit == 1)
				continue;
			assert(lit != -1);
			idx = abs(lit);
			assert(lglpeek(&lgl->elm->mark, idx) == lglsgn(lit));
			lglpoke(&lgl->elm->mark, idx, 0);
		}
		if (lgl->limits->elm.steps <= lgl->stats->elm.steps) {
			LOG(2, "maximum number of steps in elimination exhausted");
			return 0;
		}
	}
	assert(lglm2i(lgl, 1) == ip);
	if (limit < 0) {
		LOG(3, "resolving away %d would increase number of clauses", ip);
		return 0;
	}
	if (limit)
		LOG(2, "resolving away %d removes %d clauses", ip, limit);
	else
		LOG(2, "resolving away %d does not change number of clauses", ip);
	return 1;
}

static void
lgldolargeve(LGL* lgl)
{
	const int *c, *d, *sop, *eop, *son, *eon, *p, *q, *start, *end;
	int lit, idx, sgn, tmp, ip, ilit, mp, npocc, nnocc;
	int clen, dlen, reslen;
	Val val;
	ip = lgl->elm->pivot;
	assert(ip);
	sop = lgl->elm->lits.start + 1;
	eop = son = lgl->elm->lits.start + lgl->elm->neglidx;
	eon = lgl->elm->lits.top;
	npocc = lglpeek(&lgl->elm->noccs, lglulit(1));
	nnocc = lglpeek(&lgl->elm->noccs, lglulit(-1));
	LOG(2, "(large) variable elimination of %d", lgl->elm->pivot);
	if (npocc < nnocc)
		start = sop, end = eop, mp = 1;
	else
		start = son, end = eon, ip = -ip, mp = -1;
	LOG(3, "will save clauses with %d for extending assignment", ip);
	for (c = start; c < end; c = p + 1) {
		INCSTEPS(elm.steps);
		if (*c == REMOVED) {
			for (p = c + 1; *p; p++)
				;
			continue;
		}
		lglepush(lgl, ip);
		for (p = c; (lit = *p); p++) {
			if (lit == mp)
				continue;
			assert(lit != -mp);
			ilit = lglm2i(lgl, lit);
			lglepush(lgl, ilit);
		}
		lglepush(lgl, 0);
	}
	lglepush(lgl, -ip);
	lglepush(lgl, 0);
	for (c = sop; c < eop; c = p + 1) {
		INCSTEPS(elm.steps);
		if (*c == REMOVED) {
			for (p = c + 1; *p; p++)
				;
			continue;
		}
		assert(lglmtstk(&lgl->resolvent));
		clen = 0;
		for (p = c; (lit = *p); p++) {
			if (lit == 1)
				continue;
			assert(lit != -1);
			idx = abs(lit);
			assert(!lglpeek(&lgl->elm->mark, idx));
			sgn = lglsgn(lit);
			lglpoke(&lgl->elm->mark, idx, sgn);
			ilit = lglm2i(lgl, lit);
			lglpushstk(lgl, &lgl->resolvent, ilit);
			clen++;
		}
		for (d = son; d < eon; d = q + 1) {
			INCSTEPS(elm.steps);
			if (*d == REMOVED) {
				for (q = d + 1; *q; q++)
					;
				continue;
			}
			INCSTEPS(elm.resolutions);
			assert(lglmtstk(&lgl->clause));
			dlen = 0;
			reslen = clen;
			for (q = d; (lit = *q); q++) {
				if (lit == -1)
					continue;
				dlen++;
				assert(lit != 1);
				idx = abs(lit), sgn = lglsgn(lit);
				tmp = lglpeek(&lgl->elm->mark, idx);
				if (tmp == sgn)
					continue;
				if (tmp == -sgn)
					break;
				ilit = lglm2i(lgl, lit);
				val = lglval(lgl, ilit);
				if (val < 0)
					continue;
				if (val > 0)
					break;
				lglpushstk(lgl, &lgl->clause, ilit);
				ilit = lglm2i(lgl, lit);
				lglpushstk(lgl, &lgl->resolvent, ilit);
				reslen++;
			}
			assert(reslen == (int)lglcntstk(&lgl->resolvent));
			if (!lit && reslen == 1) {
				goto RESOLVE;
			}
			if (lit) {
				while (*++q)
					;
			} else {
			RESOLVE:
				LOGMCLS(3, c, "resolving variable elimination 1st antecedent");
				LOGMCLS(3, d, "resolving variable elimination 2nd antecedent");
				for (p = c; (lit = *p); p++) {
					if (lit == 1)
						continue;
					assert(lit != -1);
					ilit = lglm2i(lgl, lit);
					val = lglval(lgl, ilit);
					if (val < 0)
						continue;
					if (val > 0)
						break;
					lglpushstk(lgl, &lgl->clause, ilit);
				}
				if (!lit) {
					lglpushstk(lgl, &lgl->clause, 0);
					LOGCLS(3, lgl->clause.start, "variable elimination resolvent");
					lgldrupligaddcls(lgl, REDCS);
					lgladdcls(lgl, 0, 0, 1);
				}
			}
			lglclnstk(&lgl->clause);
			assert(!*q);
			lglrststk(&lgl->resolvent, clen);
		}
		lglclnstk(&lgl->resolvent);
		for (p = c; (lit = *p); p++) {
			if (lit == 1)
				continue;
			assert(lit != -1);
			idx = abs(lit);
			assert(lglpeek(&lgl->elm->mark, idx) == lglsgn(lit));
			lglpoke(&lgl->elm->mark, idx, 0);
		}
	}
	lgleliminated(lgl, lgl->elm->pivot);
	lgl->stats->elm.large++;
}

static void
lglelimlitaux(LGL* lgl, int idx)
{
	lglelmsub(lgl);
	if (lglelmstr(lgl))
		return;
	lglelmfre(lgl);
	if (lgltrylargeve(lgl))
		lgldolargeve(lgl);
	(void)idx;
}

static int
lgls2m(LGL* lgl, int ilit)
{
	AVar* av = lglavar(lgl, ilit);
	int res = av->mark;
	if (!res) {
		res = lglcntstk(&lgl->seen) + 1;
		if (res > lgl->opts->smallvevars.val + 1)
			return 0;
		av->mark = res;
		assert(lglcntstk(&lgl->seen) == lglcntstk(&lgl->elm->m2i) - 1);
		lglpushstk(lgl, &lgl->seen, abs(ilit));
		lglpushstk(lgl, &lgl->elm->m2i, abs(ilit));
		LOG(4, "mapped internal variable %d to marked variable %d", abs(ilit), res);
	}
	if (ilit < 0)
		res = -res;
	return res;
}

static void
lglvar2funaux(int v, Fun res, int negate)
{
	uint64_t tmp;
	int i, j, p;
	assert(0 <= v && v < FUNVAR);
	if (v < 6) {
		tmp = lglbasevar2funtab[v];
		if (negate)
			tmp = ~tmp;
		for (i = 0; i < FUNQUADS; i++)
			res[i] = tmp;
	} else {
		tmp = negate ? ~0ull : 0ull;
		p = 1 << (v - 6);
		j = 0;
		for (i = 0; i < FUNQUADS; i++) {
			res[i] = tmp;
			if (++j < p)
				continue;
			tmp = ~tmp;
			j = 0;
		}
	}
}

static void
lglvar2fun(int v, Fun res)
{
	lglvar2funaux(v, res, 0);
}

static void
lglnegvar2fun(int v, Fun res)
{
	lglvar2funaux(v, res, 1);
}

static void
lglfuncpy(Fun dst, const Fun src)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		dst[i] = src[i];
}

static void
lglfalsefun(Fun res)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		res[i] = (uint64_t)0;
}

static void
lgltruefun(Fun res)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		res[i] = ~(uint64_t)0;
}

static int
lglisfalsefun(const Fun f)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		if (f[i] != (uint64_t)0)
			return 0;
	return 1;
}

static int
lglistruefun(const Fun f)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		if (f[i] != ~(uint64_t)0)
			return 0;
	return 1;
}

static void
lglorfun(Fun a, const Fun b)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		a[i] |= b[i];
}

static void
lglornegfun(Fun a, const Fun b)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		a[i] |= ~b[i];
}

static void
lglor3fun(Fun a, const Fun b, const Fun c)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		a[i] = b[i] | c[i];
}

static void
lglor3negfun(Fun a, const Fun b, const Fun c)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		a[i] = b[i] | ~c[i];
}

static void
lglandornegfun(Fun a, const Fun b, const Fun c)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		a[i] &= b[i] | ~c[i];
}

static void
lglandfun(Fun a, const Fun b)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		a[i] &= b[i];
}

static void
lgland3fun(Fun a, const Fun b, const Fun c)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		a[i] = b[i] & c[i];
}

static void
lgland3negfun(Fun a, const Fun b, const Fun c)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		a[i] = b[i] & ~c[i];
}

static void
lglsrfun(Fun a, int shift)
{
	uint64_t rest, tmp;
	int i, j, q, b, l;
	assert(0 <= shift);
	b = shift & 63;
	q = shift >> 6;
	j = 0;
	i = q;
	assert(i >= 0);
	l = 64 - b;
	while (j < FUNQUADS) {
		if (i < FUNQUADS) {
			tmp = a[i] >> b;
			rest = (b && i + 1 < FUNQUADS) ? (a[i + 1] << l) : 0ull;
			a[j] = rest | tmp;
		} else
			a[j] = 0ull;
		i++, j++;
	}
}

static void
lglslfun(Fun a, int shift)
{
	uint64_t rest, tmp;
	int i, j, q, b, l;
	assert(0 <= shift);
	b = shift & 63;
	q = shift >> 6;
	j = FUNQUADS - 1;
	i = j - q;
	l = 64 - b;
	while (j >= 0) {
		if (i >= 0) {
			tmp = a[i] << b;
			rest = (b && i > 0) ? (a[i - 1] >> l) : 0;
			a[j] = rest | tmp;
		} else
			a[j] = 0;
		i--, j--;
	}
}

static void
lgls2fun(int mlit, Fun res)
{
	int midx = abs(mlit), sidx = midx - 2;
	assert(0 <= sidx && sidx < FUNVAR);
	if (mlit < 0)
		lglnegvar2fun(sidx, res);
	else
		lglvar2fun(sidx, res);
}

static int
lglinitsmallve(LGL* lgl, int lit, Fun res)
{
	int blit, tag, red, other, other2, lidx, mlit;
	const int *p, *w, *eow, *c, *q;
	Fun cls, tmp;
	HTS* hts;
	Val val;
	assert(!lglval(lgl, lit));
	LOG(3, "initializing small variable eliminiation for %d", lit);
#ifndef NDEBUG
	mlit =
#endif
		lgls2m(lgl, lit);
	assert(abs(mlit) == 1);
	hts = lglhts(lgl, lit);
	lgltruefun(res);
	if (!hts->count)
		goto DONE;
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow; p++) {
		INCSTEPS(elm.steps);
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == LRGCS)
			continue;
		red = blit & REDCS;
		if (red)
			continue;
		lglfalsefun(cls);
		if (tag == BINCS || tag == TRNCS) {
			other = blit >> RMSHFT;
			val = lglval(lgl, other);
			assert(val <= 0);
			if (!val) {
				mlit = lgls2m(lgl, other);
				if (!mlit)
					return 0;
				lgls2fun(mlit, tmp);
				lglorfun(cls, tmp);
			}
			if (tag == TRNCS) {
				other2 = *p;
				val = lglval(lgl, other2);
				assert(val <= 0);
				if (!val) {
					mlit = lgls2m(lgl, other2);
					if (!mlit)
						return 0;
					lgls2fun(mlit, tmp);
					lglorfun(cls, tmp);
				}
			}
		} else {
			assert(tag == OCCS);
			lidx = blit >> RMSHFT;
			c = lglidx2lits(lgl, 0, lidx);
			for (q = c; (other = *q); q++) {
				if (other == lit)
					continue;
				assert(other != -lit);
				val = lglval(lgl, other);
				assert(val <= 0);
				if (!val) {
					mlit = lgls2m(lgl, other);
					if (!mlit)
						return 0;
					lgls2fun(mlit, tmp);
					lglorfun(cls, tmp);
				}
			}
		}
		assert(!lglisfalsefun(cls));
		assert(!lglistruefun(cls));
		lglandfun(res, cls);
		INCSTEPS(elm.copies);
	}
DONE:
	return 1;
}

static void
lglresetsmallve(LGL* lgl)
{
	lglclnstk(&lgl->elm->m2i);
	lglclnstk(&lgl->elm->clv);
	lglpopnunmarkstk(lgl, &lgl->seen);
}

static void
lglsmallevalcls(unsigned cls, Fun res)
{
	Fun tmp;
	int v;
	lglfalsefun(res);
	for (v = 0; v < FUNVAR; v++) {
		if (cls & (1 << (2 * v + 1))) {
			lglvar2fun(v, tmp);
			lglornegfun(res, tmp);
		} else if (cls & (1 << (2 * v))) {
			lglvar2fun(v, tmp);
			lglorfun(res, tmp);
		}
	}
}

static Cnf
lglpos2cnf(int pos)
{
	assert(pos >= 0);
	return pos;
}
static Cnf
lglsize2cnf(int s)
{
	assert(s >= 0);
	return ((Cnf)s) << 32;
}
static int
lglcnf2pos(Cnf cnf)
{
	return cnf & 0xfffffll;
}
static int
lglcnf2size(Cnf cnf)
{
	return cnf >> 32;
}

static Cnf
lglcnf(int pos, int size)
{
	return lglpos2cnf(pos) | lglsize2cnf(size);
}

static void
lglsmallevalcnf(LGL* lgl, Cnf cnf, Fun res)
{
	Fun tmp;
	int i, n, p, cls;
	p = lglcnf2pos(cnf);
	n = lglcnf2size(cnf);
	lgltruefun(res);
	for (i = 0; i < n; i++) {
		cls = lglpeek(&lgl->elm->clv, p + i);
		lglsmallevalcls(cls, tmp);
		lglandfun(res, tmp);
	}
}

static void
lglnegcofactorfun(const Fun f, int v, Fun res)
{
	Fun mask, masked;
	lglvar2fun(v, mask);
	lgland3negfun(masked, f, mask);
	lglfuncpy(res, masked);
	lglslfun(masked, (1 << v));
	lglorfun(res, masked);
}

static void
lglposcofactorfun(const Fun f, int v, Fun res)
{
	Fun mask, masked;
	lglvar2fun(v, mask);
	lgland3fun(masked, f, mask);
	lglfuncpy(res, masked);
	lglsrfun(masked, (1 << v));
	lglorfun(res, masked);
}

static int
lglsmallfundeps0(const Fun f)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		if (((f[i] & 0xaaaaaaaaaaaaaaaaull) >> 1) != (f[i] & 0x5555555555555555ull))
			return 1;
	return 0;
}

static int
lglsmallfundeps1(const Fun f)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		if (((f[i] & 0xccccccccccccccccull) >> 2) != (f[i] & 0x3333333333333333ull))
			return 1;
	return 0;
}

static int
lglsmallfundeps2(const Fun f)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		if (((f[i] & 0xf0f0f0f0f0f0f0f0ull) >> 4) != (f[i] & 0x0f0f0f0f0f0f0f0full))
			return 1;
	return 0;
}

static int
lglsmallfundeps3(const Fun f)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		if (((f[i] & 0xff00ff00ff00ff00ull) >> 8) != (f[i] & 0x00ff00ff00ff00ffull))
			return 1;
	return 0;
}

static int
lglsmallfundeps4(const Fun f)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		if (((f[i] & 0xffff0000ffff0000ull) >> 16) != (f[i] & 0x0000ffff0000ffffull))
			return 1;
	return 0;
}

static int
lglsmallfundeps5(const Fun f)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		if (((f[i] & 0xffffffff00000000ull) >> 32) != (f[i] & 0x00000000ffffffffull))
			return 1;
	return 0;
}

static int
lglsmallfundepsgen(const Fun f, int min)
{
	const int c = (1 << (min - 6));
	int i, j;
	assert(min >= 6);
	for (i = 0; i < FUNQUADS; i += (1 << (min - 5)))
		for (j = 0; j < c; j++)
			if (f[i + j] != f[i + c + j])
				return 1;
	return 0;
}

static int
lglsmalltopvar(const Fun f, int min)
{
	int i;
	if (min == 0 && lglsmallfundeps0(f))
		return 0;
	if (min == 1 && lglsmallfundeps1(f))
		return 1;
	if (min == 2 && lglsmallfundeps2(f))
		return 2;
	if (min == 3 && lglsmallfundeps3(f))
		return 3;
	if (min == 4 && lglsmallfundeps4(f))
		return 4;
	if (min == 5 && lglsmallfundeps5(f))
		return 5;
	for (i = lglmax(6, min); i <= FUNVAR - 2; i++)
		if (lglsmallfundepsgen(f, i))
			return i;
	return i;
}

static Cnf
lglsmalladdlit2cnf(LGL* lgl, Cnf cnf, int lit)
{
	int p, m, q, n, i, cls;
	Cnf res;
	p = lglcnf2pos(cnf);
	m = lglcnf2size(cnf);
	q = lglcntstk(&lgl->elm->clv);
	for (i = 0; i < m; i++) {
		cls = lglpeek(&lgl->elm->clv, p + i);
		assert(!(cls & lit));
		cls |= lit;
		lglpushstk(lgl, &lgl->elm->clv, cls);
	}
	n = lglcntstk(&lgl->elm->clv) - q;
	res = lglcnf(q, n);
	return res;
}

#ifndef NDEBUG
static int
lglefun(const Fun a, const Fun b)
{
	int i;
	for (i = 0; i < FUNQUADS; i++)
		if (a[i] & ~b[i])
			return 0;
	return 1;
}
#endif

// The dual version of Minato's algorithm for computing an irredundant
// product of sums.   The original algorithm computes an irredundant sum of
// products. It uses BDDs for representing boolean functions and ZDDs for
// manipulating sum of product.  We work with function tables stored as bit
// maps and plain CNFs stored as lists of clauses instead.

static Cnf
lglsmallipos(LGL* lgl, const Fun U, const Fun L, int min)
{
	Fun U0, U1, L0, L1, Unew, ftmp;
	Cnf c0, c1, cstar, ctmp, res;
	int x, y, z;
	assert(lglefun(L, U));
	if (lglistruefun(U))
		return TRUECNF;
	if (lglisfalsefun(L))
		return FALSECNF;
	lgl->stats->elm.ipos++;
	assert(min < (int)lglcntstk(&lgl->elm->m2i));
	y = lglsmalltopvar(U, min);
	z = lglsmalltopvar(L, min);
	INCSTEPS(elm.steps);
	x = (y < z) ? y : z;
	assert(x < FUNVAR);
	lglnegcofactorfun(U, x, U0);
	lglposcofactorfun(U, x, U1);
	lglnegcofactorfun(L, x, L0);
	lglposcofactorfun(L, x, L1);
	lglor3negfun(ftmp, U0, L1);
	c0 = lglsmallipos(lgl, ftmp, L0, min + 1);
	lglor3negfun(ftmp, U1, L0);
	c1 = lglsmallipos(lgl, ftmp, L1, min + 1);
	lglsmallevalcnf(lgl, c0, ftmp);
	lglor3negfun(Unew, U0, ftmp);
	lglsmallevalcnf(lgl, c1, ftmp);
	lglandornegfun(Unew, U1, ftmp);
	lglor3fun(ftmp, L0, L1);
	cstar = lglsmallipos(lgl, Unew, ftmp, min + 1);
	assert(cstar != FALSECNF);
	ctmp = lglsmalladdlit2cnf(lgl, c1, (1 << (2 * x + 1)));
	res = lglcnf2pos(ctmp);
	ctmp = lglsmalladdlit2cnf(lgl, c0, (1 << (2 * x)));
	if (res == TRUECNF)
		res = lglcnf2pos(ctmp);
	ctmp = lglsmalladdlit2cnf(lgl, cstar, 0);
	if (res == TRUECNF)
		res = lglcnf2pos(ctmp);
	res |= lglsize2cnf(lglcntstk(&lgl->elm->clv) - res);
	return res;
}

static void
lglsmallve(LGL* lgl, Cnf cnf)
{
	int* soc = lgl->elm->clv.start + lglcnf2pos(cnf);
	int* eoc = soc + lglcnf2size(cnf);
	int *p, cls, v, lit, trivial;
	Val val;
	for (p = soc; !lgl->mt && p < eoc; p++) {
		cls = *p;
		assert(lglmtstk(&lgl->clause));
		trivial = 0;
		for (v = 0; v < FUNVAR; v++) {
			if (cls & (1 << (2 * v + 1)))
				lit = -lglm2i(lgl, v + 2);
			else if (cls & (1 << (2 * v)))
				lit = lglm2i(lgl, v + 2);
			else
				continue;
			val = lglval(lgl, lit);
			if (val < 0)
				continue;
			if (val > 0)
				trivial = 1;
			lglpushstk(lgl, &lgl->clause, lit);
		}
		if (!trivial) {
			INCSTEPS(elm.resolutions);
			lglpushstk(lgl, &lgl->clause, 0);
			LOGCLS(3, lgl->clause.start, "small elimination resolvent");
			lgldrupligaddcls(lgl, REDCS);
			lgladdcls(lgl, 0, 0, 1);
		}
		lglclnstk(&lgl->clause);
	}
}

static int
lglsmallisunitcls(LGL* lgl, int cls)
{
	int fidx, fsign, flit, mlit, ilit;
	ilit = 0;
	for (fidx = 0; fidx < FUNVAR; fidx++)
		for (fsign = 0; fsign <= 1; fsign++) {
			flit = 1 << (2 * fidx + fsign);
			if (!(cls & flit))
				continue;
			if (ilit)
				return 0;
			mlit = (fidx + 2) * (fsign ? -1 : 1);
			ilit = lglm2i(lgl, mlit);
		}
	return ilit;
}

static int
lglsmallcnfunits(LGL* lgl, Cnf cnf)
{
	int p, m, i, res, cls, ilit;
	p = lglcnf2pos(cnf);
	m = lglcnf2size(cnf);
	res = 0;
	for (i = 0; i < m; i++) {
		cls = lglpeek(&lgl->elm->clv, p + i);
		ilit = lglsmallisunitcls(lgl, cls);
		if (!ilit)
			continue;
		assert(lglval(lgl, ilit) >= 0);
		lglunit(lgl, ilit);
		res++;
	}
	return res;
}

static int
lgltrysmallve(LGL* lgl, int idx)
{
	int res, new, old, units;
	Fun pos, neg, fun;
	EVar* ev;
	Cnf cnf;
	assert(lglmtstk(&lgl->elm->m2i));
	assert(lglmtstk(&lgl->seen));
	assert(lglmtstk(&lgl->elm->clv));
	if (!lgl->opts->smallve.val)
		return 0;
	if (lgl->opts->smallvewait.val && !lgl->elmrtc)
		return 0;
	lglpushstk(lgl, &lgl->elm->m2i, 0);
	lglpushstk(lgl, &lgl->elm->clv, 0);
	res = 0;
	if (lglinitsmallve(lgl, idx, pos) && lglinitsmallve(lgl, -idx, neg)) {
		lglor3fun(fun, pos, neg);
		cnf = lglsmallipos(lgl, fun, fun, 0);
		new = lglcnf2size(cnf);
		units = lglsmallcnfunits(lgl, cnf);
		assert(units <= new);
		new -= units;
		ev = lglevar(lgl, idx);
		old = ev->occ[0] + ev->occ[1];
		LOG(2, "small elimination of %d replaces %d old with %d new clauses and %d units", idx, old, new, units);
		lgl->stats->elm.small.tried++;
		if (new > old) {
			if (units > 0) {
				LOG(2,
					"unforced small elimination of %d would add %d clauses but produces %d units",
					idx,
					new - old,
					units);
				res = 1;
			} else {
				LOG(2, "unforced small elimination of %d would add %d clauses considered failed", idx, new - old);
				lgl->stats->elm.small.failed++;
			}
		} else {
			LOG(2, "small elimination of %d removes %d clauses thus considered promising", idx, old - new);
			lglepusheliminated(lgl, idx);
			lglflushclauses(lgl, idx);
			lglflushclauses(lgl, -idx);
			lglsmallve(lgl, cnf);
			lgl->stats->elm.small.elm++;
			res = 1;
		}
	} else
		LOG(2, "too many variables for small elimination");
	lglresetsmallve(lgl);
	return res;
}

static int
lgl2manyoccs4elm(LGL* lgl, int lit)
{
	return lglocc(lgl, lit) > lglfactor(lgl, lgl->opts->elmocclim.val, lgl->stats->elm.count);
}

static int
lglchkoccs4elmlit(LGL* lgl, int lit)
{
	int blit, tag, red, other, other2, lidx, size, lits, count;
	const int *p, *w, *eow, *c, *l;
	int64_t litslim;
	HTS* hts;
	if (lgl->opts->elmfull.val)
		return 1;
	count = lgl->stats->elm.count;
	litslim = lglfactor(lgl, lgl->opts->elmlitslim.val, count);
	hts = lglhts(lgl, lit);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	lits = 0;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		red = blit & REDCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (red || tag == LRGCS)
			continue;
		if (tag == BINCS) {
			other = blit >> RMSHFT;
			if (lgl2manyoccs4elm(lgl, other))
				return 0;
			lits += 2;
		} else if (tag == TRNCS) {
			other = blit >> RMSHFT;
			if (lgl2manyoccs4elm(lgl, other))
				return 0;
			other2 = *p;
			if (lgl2manyoccs4elm(lgl, other2))
				return 0;
			lits += 3;
		} else {
			assert(tag == OCCS);
			lidx = blit >> RMSHFT;
			c = lglidx2lits(lgl, 0, lidx);
			size = 0;
			for (l = c; (other = *l); l++) {
				if (lgl2manyoccs4elm(lgl, other))
					return 0;
				if (++size > lglfactor(lgl, lgl->opts->elmclslim.val, count))
					return 0;
			}
			lits += size;
		}
		if (lits > litslim)
			return 0;
	}
	return 1;
}

static int
lglchkoccs4elm(LGL* lgl, int idx)
{
	int res;
	if ((res = lglforcedve(lgl, idx)) > 0)
		return 1;
	if (res < 0)
		return 0;
	if (lgl2manyoccs4elm(lgl, idx))
		return 0;
	if (lgl2manyoccs4elm(lgl, -idx))
		return 0;
	if (!lglchkoccs4elmlit(lgl, idx))
		return 0;
	if (!lglchkoccs4elmlit(lgl, -idx))
		return 0;
	return 1;
}

static int
lglispure(LGL* lgl, int lit)
{
	if (!lgl->opts->pure.val)
		return 0;
	if (lglifrozen(lgl, lit))
		return 0;
	return !lglocc(lgl, -lit);
}

static int
lglpurelit(LGL* lgl, int lit)
{
	int res;
	LOG(1, "pure literal %d", lit);
	assert(!lglocc(lgl, -lit));
	res = lglflushclauses(lgl, lit);
	assert(lgl->blocking || lgl->eliminating);
	if (lgl->blocking) {
		lgl->stats->blk.pure++;
		lgl->stats->blk.clauses += res;
		ADDSTEPS(blk.steps, res);
	}
	if (lgl->eliminating) {
		lgl->stats->elm.pure++;
		ADDSTEPS(elm.steps, res);
	}
	lglepusheliminated(lgl, lit);
	return res;
}

static void
lglelimlit(LGL* lgl, int idx)
{
	int forced;
	if (!lglisfree(lgl, idx))
		return;
	if (lglispure(lgl, idx)) {
		(void)lglpurelit(lgl, idx);
		return;
	}
	if (lglispure(lgl, -idx)) {
		(void)lglpurelit(lgl, -idx);
		return;
	}
	if (!lglchkoccs4elm(lgl, idx))
		return;
	LOG(2, "trying to eliminate %d", idx);
	if ((forced = lglforcedve(lgl, idx)) < 0)
		return;
	if (!forced && lgltrysmallve(lgl, idx))
		return;
	lglinitecls(lgl, idx);
	lglelimlitaux(lgl, idx);
	if (lgl->elm->pivot)
		lglrstecls(lgl);
}

static int
lglblockcls(LGL* lgl, int lit)
{
	int blit, tag, red, other, other2, lidx, val, count, size;
	const int *p, *w, *eow, *c, *l;
	int bc = lgl->stats->blk.count;
	HTS* hts;
	INCSTEPS(blk.steps);
	hts = lglhts(lgl, lit);
	if (!hts->count)
		return 1;
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	count = 0;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == LRGCS)
			continue;
		red = blit & REDCS;
		if (red)
			continue;
		count++;
		lgl->stats->blk.res++;
		INCSTEPS(blk.steps);
		if (tag == BINCS || tag == TRNCS) {
			other = blit >> RMSHFT;
			val = lglmarked(lgl, other);
			if (val < 0)
				continue;
			if (tag == TRNCS) {
				other2 = *p;
				val = lglmarked(lgl, other2);
				if (val < 0)
					continue;
			}
		} else {
			assert(tag == OCCS);
			lidx = blit >> RMSHFT;
			c = lglidx2lits(lgl, 0, lidx);
			size = 0;
			for (l = c; (other = *l); l++) {
				val = lglmarked(lgl, other);
				if (++size > lglfactor(lgl, lgl->opts->blkclslim.val, bc))
					return 0;
				if (val < 0)
					break;
			}
			if (other)
				continue;
		}
		return 0;
	}
	LOG(3, "resolved %d trivial resolvents on %d", count, lit);
	return 1;
}

static int
lgl2manyoccs4blk(LGL* lgl, int lit)
{
	return lglhts(lgl, lit)->count > lglfactor(lgl, lgl->opts->blkocclim.val, lgl->stats->blk.count);
}

static int
lgldonotblocklit(LGL* lgl, int lit)
{
	int pocc = lglocc(lgl, lit);
	int nocc = lglocc(lgl, -lit);
	int count = lgl->stats->blk.count;
	if (pocc >= lglfactor(lgl, lgl->opts->blkocclim1.val, count))
		return 1;
	if (nocc >= lglfactor(lgl, lgl->opts->blkocclim1.val, count))
		return 1;
	if (pocc < lglfactor(lgl, lgl->opts->blkocclim2.val, count))
		return 0;
	if (nocc < lglfactor(lgl, lgl->opts->blkocclim2.val, count))
		return 0;
	return 1;
}

static int
lglblocklit(LGL* lgl, int lit, Stk* stk)
{
	int blit, tag, red, blocked, other, other2, lidx, count, size;
	int bc = lgl->stats->blk.count;
	int *p, *w, *eow, *c, *l;
	HTS* hts;
	if (lglval(lgl, lit))
		return 0;
	if (lgldonotblocklit(lgl, lit))
		return 0;
	if (lgl2manyoccs4blk(lgl, lit))
		return 0;
	hts = lglhts(lgl, lit);
	assert(!lgl->opts->pure.val || hts->count > 0);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	count = 0;
	assert(lglmtstk(stk + 2) && lglmtstk(stk + 3) && lglmtstk(stk + 4));
	for (p = w; p < eow; p++) {
		if (INCSTEPS(blk.steps) >= lgl->limits->blk.steps)
			break;
		if (lglterminate(lgl))
			break;
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == LRGCS)
			continue;
		red = blit & REDCS;
		if (red)
			continue;
		assert(lglmtstk(&lgl->seen));
		blocked = 0;
		if (tag == BINCS || tag == TRNCS) {
			if (!lgl->opts->blksmall.val)
				continue;
			other = blit >> RMSHFT;
			if (lgl2manyoccs4blk(lgl, other))
				continue;
			lglpushnmarkseen(lgl, other);
			if (tag == TRNCS) {
				other2 = *p;
				if (lgl2manyoccs4blk(lgl, other2))
					goto CONTINUE;
				lglpushnmarkseen(lgl, other2);
			}
		} else {
			assert(tag == OCCS);
			if (!lgl->opts->blklarge.val)
				continue;
			lidx = blit >> RMSHFT;
			c = lglidx2lits(lgl, 0, lidx);
			size = 0;
			for (l = c; (other = *l); l++) {
				if (other == lit)
					continue;
				if (lgl2manyoccs4blk(lgl, other))
					goto CONTINUE;
				if (++size > lglfactor(lgl, lgl->opts->blkclslim.val, bc))
					goto CONTINUE;
				lglpushnmarkseen(lgl, other);
			}
		}
		blocked = lglblockcls(lgl, -lit);
	CONTINUE:
		lglpopnunmarkstk(lgl, &lgl->seen);
		if (!blocked)
			continue;
		if (tag == BINCS) {
			other = blit >> RMSHFT;
			lglpushstk(lgl, stk + 2, other);
		} else if (tag == TRNCS) {
			other = blit >> RMSHFT;
			lglpushstk(lgl, stk + 3, other);
			other2 = *p;
			lglpushstk(lgl, stk + 3, other2);
		} else {
			assert(tag == OCCS);
			lidx = blit >> RMSHFT;
			lglpushstk(lgl, stk + 4, lidx);
		}
	}
	while (!lglmtstk(stk + 2)) {
		if (INCSTEPS(blk.steps) >= lgl->limits->blk.steps)
			break;
		count++;
		other = lglpopstk(stk + 2);
		LOG(2, "blocked binary clause %d %d on %d", lit, other, lit);
		lglrmbcls(lgl, lit, other, 0);
		lglepush(lgl, lit);
		lglepush(lgl, other);
		lglepush(lgl, 0);
		if (!lglmvbcls(lgl, lit, other))
			lgldrupligdelclsarg(lgl, lit, other, 0);
	}
	while (!lglmtstk(stk + 3)) {
		if (INCSTEPS(blk.steps) >= lgl->limits->blk.steps)
			break;
		count++;
		other2 = lglpopstk(stk + 3);
		other = lglpopstk(stk + 3);
		LOG(2, "blocked ternary clause %d %d %d on %d", lit, other, other2, lit);
		lglrmtcls(lgl, lit, other, other2, 0);
		lglepush(lgl, lit);
		lglepush(lgl, other);
		lglepush(lgl, other2);
		lglepush(lgl, 0);
		if (!lglmvtcls(lgl, lit, other, other2))
			lgldrupligdelclsarg(lgl, lit, other, other2, 0);
	}
	while (!lglmtstk(stk + 4)) {
		if (INCSTEPS(blk.steps) >= lgl->limits->blk.steps)
			break;
		lidx = lglpopstk(stk + 4);
		count++;
		c = lglidx2lits(lgl, 0, lidx);
		LOGCLS(2, c, "blocked on %d large clause", lit);
		lgldrupligdelclsaux(lgl, c);
		lglepush(lgl, lit);
		for (l = c; (other = *l); l++)
			if (other != lit)
				lglepush(lgl, other);
		lglepush(lgl, 0);
		lglrmlcls(lgl, lidx, 0);
	}
	LOG(2, "found %d blocked clauses with %d", count, lit);
	lgl->stats->blk.clauses += count;
	if (count > 0)
		lglblockinglit(lgl, lit);
	lglclnstk(stk + 2), lglclnstk(stk + 3), lglclnstk(stk + 4);
	return count;
}

static void
lglsetblklim(LGL* lgl)
{
	int64_t limit, irrlim;
	int pen, szpen;
	if (lgl->opts->blkrtc.val) {
		lgl->limits->blk.steps = LLMAX;
		lglprt(lgl, 1, "[block-%d] really no limit (run to completion)", lgl->stats->blk.count);
	} else {
		limit = (lgl->opts->blkreleff.val * lglvisearch(lgl)) / 1000;
		if (limit < lgl->opts->blkmineff.val)
			limit = lgl->opts->blkmineff.val;
		if (lgl->opts->blkmaxeff.val >= 0 && limit > lgl->opts->blkmaxeff.val)
			limit = lgl->opts->blkmaxeff.val;
		if (lgl->stats->blk.count <= 1 && lgl->opts->boost.val && lglrem(lgl) < lgl->opts->blkboostvlim.val) {
			lglprt(lgl, 1, "[block-%d] boosting limit by %d", lgl->stats->blk.count, lgl->opts->blkboost.val);
			limit *= lgl->opts->blkboost.val;
		}
		limit >>= (pen = lgl->limits->blk.pen + (szpen = lglszpen(lgl)));
		irrlim = (lgl->stats->irr.clauses.cur / 2) >> szpen;
		if (lgl->opts->irrlim.val && limit < irrlim) {
			limit = irrlim;
			lglprt(lgl,
				   1,
				   "[block-%d] limit of %lld steps based on %d irredundant clauses penalty %d",
				   lgl->stats->blk.count,
				   (LGLL)limit,
				   lgl->stats->irr.clauses.cur,
				   szpen);
		} else
			lglprt(lgl,
				   1,
				   "[block-%d] limit of %lld steps penalty %d = %d + %d",
				   lgl->stats->blk.count,
				   (LGLL)limit,
				   pen,
				   lgl->limits->blk.pen,
				   szpen);
		lgl->limits->blk.steps = lgl->stats->blk.steps + limit;
	}
}

static int
lgleschedrem(LGL* lgl, int this_time)
{
	int idx, res = 0, count;
	const char* str;
	AVar* av;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (lglifrozen(lgl, idx))
			continue;
		if (!lglisfree(lgl, idx))
			continue;
		av = lglavar(lgl, idx);
		if (lgl->eliminating && av->donotelm)
			continue;
		if (lgl->blocking && av->donotblk)
			continue;
		res++;
	}
	assert(lgl->eliminating || lgl->blocking);
	if (lgl->eliminating)
		count = lgl->stats->elm.count, str = "elim";
	else
		assert(lgl->blocking), count = lgl->stats->blk.count, str = "block";
	if (res)
		lglprt(lgl,
			   1,
			   "[%s-%d] %d variables %.0f%% %s time",
			   str,
			   count,
			   res,
			   lglpcnt(res, lglrem(lgl)),
			   this_time ? "will be scheduled this" : "remain to be tried next");
	else {
		lglprt(lgl, 1, "[%s-%d] no untried remaining variables left", str, count);
		for (idx = 2; idx < lgl->nvars; idx++) {
			av = lglavar(lgl, idx);
			if (lgl->eliminating)
				av->donotelm = 0;
			if (lgl->blocking)
				av->donotblk = 0;
		}
	}
	return res;
}

static void
lglsetdonotesched(LGL* lgl, int completed)
{
	AVar* av;
	EVar* ev;
	int idx;
	assert(lgl->eliminating + lgl->blocking == 1);
	for (idx = 2; idx < lgl->nvars; idx++) {
		av = lglavar(lgl, idx);
		ev = lglevar(lgl, idx);
		if (lgl->eliminating) {
			if (completed)
				av->donotelm = 0;
			else if (ev->pos < 0)
				av->donotelm = 1;
		}
		if (lgl->blocking) {
			if (completed)
				av->donotblk = 0;
			else if (ev->pos < 0)
				av->donotblk = 1;
		}
	}
}

static int
lglblkdone(LGL* lgl)
{
	if (!lglsmallirr(lgl))
		return 1;
	if (lglterminate(lgl))
		return 1;
	if (lglmtstk(&lgl->esched))
		return 1;
	if (lgl->stats->blk.steps >= lgl->limits->blk.steps)
		return 1;
	return 0;
}

static void
lglblock(LGL* lgl)
{
	int oldrem = lgl->blkrem, oldall = lgl->blkall;
	int oldirr = lgl->stats->irr.clauses.cur;
	int idx, count, all, rem, success;
	Stk blocked[5];
	assert(lglsmallirr(lgl));
	assert(!lgl->simp);
	assert(!lgl->dense);
	assert(!lgl->eliminating);
	assert(!lgl->blocking);
	assert(!lgl->occs);
	lglstart(lgl, &lgl->times->block);
	if (lgl->level)
		lglbacktrack(lgl, 0);
	lgl->simp = lgl->blocking = lgl->occs = 1;
	lgl->stats->blk.count++;
	lglgc(lgl);
	assert(lgl->frozen);
	assert(!(oldall && !oldrem));
	all = !oldrem || !oldall;
	if (all)
		lglprt(lgl, 1, "[block-%d] scheduling all variables this time", lgl->stats->blk.count);
	else if (!lgleschedrem(lgl, 1))
		all = 1, oldrem = 0;
	if (!all)
		assert(!lgl->donotsched), lgl->donotsched = 1;
	lgldense(lgl, 1);
	if (!all)
		assert(lgl->donotsched), lgl->donotsched = 0;
	lglsetblklim(lgl);
	CLR(blocked);
	count = 0;
	if (!lgl->opts->blkresched.val)
		assert(!lgl->donotsched), lgl->donotsched = 1;
	while (!lglblkdone(lgl)) {
		idx = lglpopesched(lgl);
		lglavar(lgl, idx)->donotblk = 1;
		if (!lglisfree(lgl, idx))
			continue;
		if (lglispure(lgl, -idx))
			count += lglpurelit(lgl, -idx);
		else if (lglispure(lgl, idx))
			count += lglpurelit(lgl, idx);
		else {
			if (lglocc(lgl, -idx) > lglocc(lgl, idx))
				idx = -idx;
			count += lglblocklit(lgl, idx, blocked);
			count += lglblocklit(lgl, -idx, blocked);
		}
	}
	if (!lgl->opts->blkresched.val)
		assert(lgl->donotsched), lgl->donotsched = 0;
	rem = lglcntstk(&lgl->esched);
	if (!rem) {
		lglprt(lgl, 1, "[block-%d] fully completed", lgl->stats->blk.count);
		lgl->blkrtc = 1;
	} else {
		lglprt(lgl,
			   1,
			   "[block-%d] incomplete %d not tried %.0f%%",
			   lgl->stats->blk.count,
			   rem,
			   lglpcnt(rem, lgl->nvars - 2));
	}
	lglsetdonotesched(lgl, !rem);
	lglrelstk(lgl, &lgl->esched);
	lglsparse(lgl);
	lglgc(lgl);
	lglrelstk(lgl, blocked + 2);
	lglrelstk(lgl, blocked + 3);
	lglrelstk(lgl, blocked + 4);
	lgl->blkrem = rem > 0;
	lgl->blkall = all && lgl->blkrem;
	lglprt(lgl, 1, "[block-%d] transition to [ all %d rem %d ] state", lgl->stats->blk.count, lgl->blkall, lgl->blkrem);
	assert(lgl->simp && lgl->blocking && lgl->occs);
	lgl->blocking = lgl->simp = lgl->occs = 0;
	lgl->stats->irrprgss += count;
	lglprt(lgl, 1, "[block-%d] eliminated %d blocked clauses", lgl->stats->blk.count, count);
	if (!lgl->blkrtc && lgl->stats->blk.count <= lgl->opts->blksuccessmaxwortc.val) {
		success = 1;
		lglprt(lgl, 1, "[block-%d] considered successful since not run to completion yet", lgl->stats->blk.count);
	} else if (count) {
		success = (oldirr / lgl->opts->blksuccessrat.val <= count);
		if (!success)
			lglprt(lgl,
				   1,
				   "[block-%d] %d < 1/%d * %d = %d considered unsuccessful",
				   lgl->stats->blk.count,
				   count,
				   lgl->opts->blksuccessrat.val,
				   oldirr,
				   oldirr / lgl->opts->blksuccessrat.val);
	} else
		success = 0;
	LGLUPDPEN(blk, success);
	lglrep(lgl, 2, 'k');
	lglstop(lgl);
	assert(!lgl->mt);
}

static void
lglsetccelim(LGL* lgl)
{
	int count = lgl->stats->cce.count - lgl->opts->cceboostdel.val;
	int64_t limit, irrlim;
	int pen, szpen;
	if (lgl->opts->ccertc.val > 1) {
		lgl->limits->cce.steps = LLMAX;
		lglprt(lgl, 1, "[cce-%d] really no limit (run to completion)", lgl->stats->cce.count);
	} else if (lgl->opts->ccertc.val ||
			   (count > 0 && lglrem(lgl) < lgl->opts->ccertcintvlim.val && !(count % lgl->opts->ccertcint.val))) {
		limit = 4000000000ll;
		lgl->limits->cce.steps = lgl->stats->cce.steps + limit;
		lglprt(lgl, 1, "[cce-%d] almost no limit of %lld steps", lgl->stats->cce.count, (LGLL)limit);
	} else {
		limit = (lgl->opts->ccereleff.val * lglvisearch(lgl)) / 1000;
		if (limit < lgl->opts->ccemineff.val)
			limit = lgl->opts->ccemineff.val;
		if (lgl->opts->ccemaxeff.val >= 0 && limit > lgl->opts->ccemaxeff.val)
			limit = lgl->opts->ccemaxeff.val;
		if (count > 0 && (count <= 1 || !(count % lgl->opts->cceboostint.val)) && lgl->opts->boost.val &&
			lgl->nvars < lgl->opts->cceboostvlim.val) {
			lglprt(lgl, 1, "[cce-%d] boosting limit by %d", lgl->stats->cce.count, lgl->opts->cceboost.val);
			limit *= lgl->opts->cceboost.val;
		}
		limit >>= (pen = lgl->limits->cce.pen + (szpen = lglszpen(lgl)));
		irrlim = (lgl->stats->irr.clauses.cur) >> szpen;
		if (lgl->opts->irrlim.val && limit < irrlim) {
			limit = irrlim;
			lglprt(lgl,
				   1,
				   "[cce-%d] limit of %lld steps based on %d irredundant clauses penalty %d",
				   lgl->stats->cce.count,
				   (LGLL)limit,
				   lgl->stats->irr.clauses.cur,
				   szpen);
		} else
			lglprt(lgl,
				   1,
				   "[cce-%d] limit of %lld steps penalty %d = %d + %d",
				   lgl->stats->cce.count,
				   (LGLL)limit,
				   pen,
				   lgl->limits->cce.pen,
				   szpen);
		lgl->limits->cce.steps = lgl->stats->cce.steps + limit;
	}
}

#define CCELOGLEVEL 2

static void
lglsignedmark2(LGL* lgl, int lit)
{
	AVar* av = lglavar(lgl, lit);
	int bit = 1 << (2 + (lit < 0));
	if (av->mark & bit)
		return;
	av->mark |= bit;
}

static void
lglsignedunmark2(LGL* lgl, int lit)
{
	AVar* av = lglavar(lgl, lit);
	int bit = 1 << (2 + (lit < 0));
	if (!(av->mark & bit))
		return;
	av->mark &= ~bit;
}

static int
lglsignedmarked2(LGL* lgl, int lit)
{
	AVar* av = lglavar(lgl, lit);
	int bit = 1 << (2 + (lit < 0));
	return av->mark & bit;
}

static int
lglabcecls(LGL* lgl, int lit, const int* c)
{
	int other, found = 0;
	const int* p;
	for (p = c; (other = *p); p++)
		if (other == -lit)
			found++;
		else if (lglsignedmarked(lgl, -other))
			return 1;
	assert(found == 1);
	return 0;
}

static int
lglabce(LGL* lgl, int lit)
{
	const int *p, *w, *eow, *c;
	int blit, tag, other, cls[4];
	HTS* hts;
	assert(!lglifrozen(lgl, lit));
	hts = lglhts(lgl, -lit);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	cls[0] = -lit, cls[3] = 0;
	for (p = w; p < eow; p++) {
		if (lgl->limits->cce.steps <= INCSTEPS(cce.steps))
			return 0;
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (blit & REDCS)
			continue;
		if (tag == LRGCS)
			continue;
		other = blit >> RMSHFT;
		cls[1] = other;
		if (tag == BINCS)
			cls[2] = 0, c = cls;
		else if (tag == TRNCS)
			cls[2] = *p, c = cls;
		else
			assert(tag == OCCS), c = lglidx2lits(lgl, 0, other);
		if (!lglabcecls(lgl, lit, c))
			return 0;
	}
	return 1;
}

static int
lglcceclause(LGL* lgl, const int* c, int igntag, int cce)
{
	int other, res, nextala, nextcla, lit, blit, tag, other2, i, j, n;
	int ala, first, old, prev, steps, lidx;
	int *newtop, *d, *q, *r;
	const int *p, *eow, *w;
	ptrdiff_t delta;
	HTS* hts;
	LOGCLS(CCELOGLEVEL, c, "trying CCE on clause");
	assert(lglmtstk(&lgl->cce->extend));
	assert(lglmtstk(&lgl->cce->cla));
	assert(lglmtstk(&lgl->seen));
	for (p = c; (other = *p); p++) {
		assert(!lglmarked(lgl, other));
		lglpushstk(lgl, &lgl->seen, other);
		lglpushstk(lgl, &lgl->cce->cla, other);
		lglsignedmark(lgl, other);
	}
	n = lglcntstk(&lgl->seen);
	for (i = n - 1; i > 0; i--) {
		j = lglrand(lgl) % (i + 1);
		if (j == i)
			continue;
		SWAP(int, lgl->seen.start[i], lgl->seen.start[j]);
		SWAP(int, lgl->cce->cla.start[i], lgl->cce->cla.start[j]);
	}
	nextcla = nextala = res = 0;
ALA:
	while (!res && nextala < (int)lglcntstk(&lgl->seen)) {
		lit = lglpeek(&lgl->seen, nextala++);
		assert(lglsignedmarked(lgl, lit));
		assert(!lglsignedmarked(lgl, -lit));
		hts = lglhts(lgl, lit);
		w = newtop = lglhts2wchs(lgl, hts);
		eow = w + hts->count;
		steps = 0;
		for (p = w; !res && p < eow; p++) {
			steps++;
			blit = *p;
			*newtop++ = blit;
			tag = blit & MASKCS;
			if (tag == TRNCS || tag == LRGCS)
				*newtop++ = *++p;
			if (tag == OCCS)
				continue;
			if (blit & REDCS)
				continue;
			other = blit >> RMSHFT;
			if (lglsignedmarked(lgl, -other))
				continue;
			if (tag == BINCS) {
				if (lglsignedmarked(lgl, other)) {
					if (igntag == BINCS) {
						if (c[0] == lit && c[1] == other)
							continue;
						if (c[1] == lit && c[0] == other)
							continue;
					}
					LOG(CCELOGLEVEL, "ALA on binary clause %d %d results in ATE", lit, other);
					res = 1;
				} else {
					assert(!lglmarked(lgl, other));
					LOG(CCELOGLEVEL, "ALA %d through binary clause %d %d", -other, lit, other);
					lglsignedmark(lgl, -other);
					lglpushstk(lgl, &lgl->seen, -other);
				}
			} else if (tag == TRNCS) {
				other2 = *p;
				if (lglsignedmarked(lgl, -other2))
					continue;
				if (lglsignedmarked(lgl, other)) {
					if (lglsignedmarked(lgl, other2)) {
						if (igntag == TRNCS) {
							if (c[0] == lit && c[1] == other && c[2] == other2)
								continue;
							if (c[0] == lit && c[2] == other && c[1] == other2)
								continue;
							if (c[1] == lit && c[0] == other && c[2] == other2)
								continue;
							if (c[1] == lit && c[2] == other && c[0] == other2)
								continue;
							if (c[2] == lit && c[0] == other && c[1] == other2)
								continue;
							if (c[2] == lit && c[1] == other && c[0] == other2)
								continue;
						}
						LOG(CCELOGLEVEL, "ALA on ternary clause %d %d %d results in ATE", lit, other, other2);
						res = 1;
					} else {
						assert(!lglmarked(lgl, other2));
						LOG(CCELOGLEVEL,
							"ALA %d through ternary clause %d %d %d (1st case)",
							-other2,
							lit,
							other,
							other2);
						lglsignedmark(lgl, -other2);
						lglpushstk(lgl, &lgl->seen, -other2);
					}
				} else if (lglsignedmarked(lgl, other2)) {
					assert(!lglmarked(lgl, other));
					LOG(CCELOGLEVEL, "ALA %d through ternary clause %d %d %d (2nd case)", -other, lit, other, other2);
					lglsignedmark(lgl, -other);
					lglpushstk(lgl, &lgl->seen, -other);
				}
			} else {
				assert(tag == LRGCS);
				d = lglidx2lits(lgl, 0, (lidx = *p));
				if (d == c)
					continue;
				if (d[0] == lit)
					SWAP(int, d[0], d[1]);
				assert(d[1] == lit);
				if (lglsignedmarked(lgl, -d[0]))
					continue;
				for (q = d + 2; (other = *q); q++) {
					if (lglsignedmarked(lgl, -other))
						break;
					if (!lglsignedmarked(lgl, other))
						break;
				}
				if (other) {
					newtop -= 2, assert(w <= newtop);
					SWAP(int, d[1], *q);
					assert(d[1] == other);
					delta = lglwchlrg(lgl, d[1], d[0], 0, lidx);
					if (delta)
						w += delta, p += delta, newtop += delta, eow += delta;
				} else if (lglsignedmarked(lgl, d[0])) {
					LOGCLS(CCELOGLEVEL, d, "ATE after ALA on large clause");
					res = 1;
				} else {
					ala = -d[0];
					assert(!lglmarked(lgl, ala));
					LOGCLS(CCELOGLEVEL, d, "ALA %d through large clause", ala);
					lglsignedmark(lgl, ala);
					lglpushstk(lgl, &lgl->seen, ala);
				}
			}
		}
		assert(newtop <= eow);
		while (p < eow)
			*newtop++ = *p++;
		lglshrinkhts(lgl, hts, newtop - w);
		if (lgl->limits->cce.steps <= ADDSTEPS(cce.steps, steps))
			goto DONE;
	}
	if (res || !lgl->opts->block.val || cce < 3)
		goto SKIPCLA;
	while (!res && nextcla < (int)lglcntstk(&lgl->cce->cla)) {
		lit = lglpeek(&lgl->cce->cla, nextcla++);
		if (lglifrozen(lgl, lit))
			continue;
		assert(lglsignedmarked(lgl, lit));
		assert(!lglsignedmarked(lgl, -lit));
		hts = lglhts(lgl, -lit);
		w = lglhts2wchs(lgl, hts);
		eow = w + hts->count;
		old = lglcntstk(&lgl->cce->cla);
		first = 1;
		for (p = w; p < eow; p++) {
			if (lgl->limits->cce.steps <= INCSTEPS(cce.steps))
				goto DONE;
			blit = *p;
			tag = blit & MASKCS;
			if (tag == TRNCS || tag == LRGCS)
				p++;
			if (tag == LRGCS)
				continue;
			if (blit & REDCS)
				continue;
			other = blit >> RMSHFT;
			if (first) {
				if (tag == BINCS) {
					if (lglsignedmarked(lgl, -other))
						continue;
					if (!lglsignedmarked(lgl, other))
						lglpushstk(lgl, &lgl->cce->cla, other);
				} else if (tag == TRNCS) {
					if (lglsignedmarked(lgl, -other))
						continue;
					if (lglsignedmarked(lgl, -(other2 = *p)))
						continue;
					if (!lglsignedmarked(lgl, other))
						lglpushstk(lgl, &lgl->cce->cla, other);
					if (!lglsignedmarked(lgl, other2))
						lglpushstk(lgl, &lgl->cce->cla, other2);
				} else {
					assert(tag == OCCS);
					d = lglidx2lits(lgl, 0, other);
					assert(d != c);
					for (q = d; (other = *q); q++) {
						if (other == -lit)
							continue;
						if (lglsignedmarked(lgl, -other))
							break;
					}
					if (other)
						continue;
					for (q = d; (other = *q); q++)
						if (other != -lit && !lglsignedmarked(lgl, other))
							lglpushstk(lgl, &lgl->cce->cla, other);
				}
				first = 0;
			} else {
				r = lgl->cce->cla.start + old;
				if (tag == BINCS) {
					if (lglsignedmarked(lgl, -other))
						continue;
					for (q = r; q < lgl->cce->cla.top; q++)
						if (*q == other)
							*r++ = *q;
				} else if (tag == TRNCS) {
					if (lglsignedmarked(lgl, -other))
						continue;
					if (lglsignedmarked(lgl, -(other2 = *p)))
						continue;
					for (q = r; q < lgl->cce->cla.top; q++)
						if (*q == other || *q == other2)
							*r++ = *q;
				} else {
					assert(tag == OCCS);
					d = lglidx2lits(lgl, 0, other);
					assert(d != c);
					for (q = d; (other = *q); q++) {
						if (other == -lit)
							continue;
						if (lglsignedmarked(lgl, -other))
							break;
					}
					if (other)
						continue;
					for (q = d; (other = *q); q++) {
						if (other == -lit)
							continue;
						assert(other != lit);
						assert(!lglsignedmarked2(lgl, other));
						assert(!lglsignedmarked2(lgl, -other));
						lglsignedmark2(lgl, other);
					}
					for (q = r; q < lgl->cce->cla.top; q++)
						if (lglsignedmarked2(lgl, (other = *q)))
							*r++ = other;
					for (q = d; (other = *q); q++) {
						if (other == -lit)
							continue;
						assert(other != lit);
						assert(lglsignedmarked2(lgl, other));
						lglsignedunmark2(lgl, other);
					}
				}
				if ((lgl->cce->cla.top = r) == lgl->cce->cla.start + old)
					break;
			}
		}
		if ((int)lglcntstk(&lgl->cce->cla) > old) {
			nextcla = 0;
			lglpushstk(lgl, &lgl->cce->extend, lit);
			for (q = lgl->cce->cla.start; q < lgl->cce->cla.start + old; q++)
				if (*q != lit)
					lglpushstk(lgl, &lgl->cce->extend, *q);
			lglpushstk(lgl, &lgl->cce->extend, 0);
		}
		for (q = lgl->cce->cla.start + old; !res && q < lgl->cce->cla.top; q++) {
			if (lglsignedmarked(lgl, -*q)) {
				LOG(CCELOGLEVEL, "CLA on %d results in ATE", *q);
				res = 1;
			} else {
				LOG(CCELOGLEVEL, "CLA %d on %d", *q, lit);
				lglpushstk(lgl, &lgl->seen, *q);
				lglsignedmark(lgl, *q);
			}
		}
		if (!res && p == eow && nextala < (int)lglcntstk(&lgl->seen))
			goto ALA;
	}
SKIPCLA:
	if (res) {
		LOGCLS(CCELOGLEVEL, c, "ATE clause");
		lgl->stats->cce.ate++;
	} else if (lgl->opts->block.val && cce >= 2) {
		for (p = lgl->cce->cla.start; p < lgl->cce->cla.top; p++)
			if (!lglifrozen(lgl, (other = *p)) && (res = lglabce(lgl, other)))
				break;
		if (res) {
			LOGCLS(CCELOGLEVEL, c, "ABCE on %d clause", other);
			lglpushstk(lgl, &lgl->cce->extend, other);
			for (p = lgl->cce->cla.start; p < lgl->cce->cla.top; p++)
				if (*p != other)
					lglpushstk(lgl, &lgl->cce->extend, *p);
			lglpushstk(lgl, &lgl->cce->extend, 0);
			lgl->stats->cce.abce++;
		}
	}
	if (res)
		lgl->stats->cce.eliminated++;
DONE:
	lglpopnunmarkstk(lgl, &lgl->seen);
	lglclnstk(&lgl->cce->cla);
	if (res && !lglmtstk(&lgl->cce->extend)) {
		assert(cce >= 2);
		assert(lgl->opts->block.val);
		prev = 0;
		for (p = lgl->cce->extend.start; p < lgl->cce->extend.top; p++) {
			lit = *p;
			lglepush(lgl, lit);
			if (!prev)
				assert(!lglifrozen(lgl, lit)), lglblockinglit(lgl, lit);
			prev = lit;
		}
	}
	lglclnstk(&lgl->cce->extend);
	return res;
}

static const char*
lglcce2str(int cce)
{
	assert(0 <= cce), assert(cce <= 3);
	if (cce == 3)
		return "ACCE";
	else if (cce == 2)
		return "ABCE";
	else if (cce == 1)
		return "ATE";
	else
		return "none";
}

static int
lglccesmallclauses(LGL* lgl, int lit)
{
	int idx = abs(lit), blit, tag, red, other, other2;
	HTS* hts = lglhts(lgl, lit);
	const int *p, *w, *eow;
	CCE* cce = lgl->cce;
	Stk* clauses;
	assert(cce);
	if (!lglisfree(lgl, lit))
		return 1;
	clauses = &cce->clauses;
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	INCSTEPS(cce.steps);
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == OCCS)
			continue;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == LRGCS)
			continue;
		red = blit & REDCS;
		if (red)
			continue;
		other = blit >> RMSHFT;
		if (abs(other) < idx)
			continue;
		if (!lglisfree(lgl, other))
			continue;
		if (tag == TRNCS) {
			other2 = *p;
			if (abs(other2) < idx)
				continue;
			if (abs(other2) < abs(other))
				continue;
			if (!lglisfree(lgl, other2))
				continue;
			lglpushstk(lgl, clauses, other2);
			cce->trn++;
		} else {
			assert(tag == BINCS);
			cce->bin++;
		}
		lglpushstk(lgl, clauses, other);
		lglpushstk(lgl, clauses, lit);
		lglpushstk(lgl, clauses, 0);
	}
	return 1;
}

static void
lglccesmall(LGL* lgl, int cce, int round)
{
	int count, valid, invalid, tried, eliminated, elim2, elim3;
	int size, lit, tag;
	const int *p, *c;
	Stk* clauses;
	assert(lgl->cce);
	clauses = &lgl->cce->clauses;
	assert(!lgl->cce->clauses.start);
	lglrandlitrav(lgl, lglccesmallclauses);
	lglfitstk(lgl, &lgl->cce->clauses);
	count = lgl->cce->bin + lgl->cce->trn;
	lglprt(lgl,
		   1,
		   "[cce-%d-%d] scheduling %d clauses = %d binary + %d ternary",
		   lgl->stats->cce.count,
		   round,
		   count,
		   lgl->cce->bin,
		   lgl->cce->trn);
	tried = eliminated = invalid = elim2 = elim3 = 0;
	for (c = clauses->start; c < clauses->top; c = p + 1) {
		if (lgl->mt)
			break;
		if (lglterminate(lgl))
			break;
		if (lgl->limits->cce.steps <= lgl->stats->cce.steps)
			break;
		valid = 1;
		for (p = c; (lit = *p); p++)
			if (!lglisfree(lgl, lit))
				valid = 0;
		if (valid) {
			tried++;
			size = p - c;
			assert(2 <= size), assert(size <= 3);
			tag = size == 2 ? BINCS : TRNCS;
			if (!lglcceclause(lgl, c, tag, cce))
				continue;
			eliminated++;
			if (size == 2) {
				if (!lglrmvbcls(lgl, c[0], c[1]))
					lgldrupligdelclsarg(lgl, c[0], c[1], 0);
				elim2++;
			} else {
				assert(size == 3);
				if (!lglrmvtcls(lgl, c[0], c[1], c[2]))
					lgldrupligdelclsarg(lgl, c[0], c[1], c[2], 0);
				elim3++;
			}
		} else
			invalid++;
	}
	assert(count >= tried + invalid);
	lglrelstk(lgl, &lgl->cce->clauses);
	if (tried)
		lglprt(lgl,
			   1,
			   "[cce-%d-%d] tried to eliminate %d small clauses %.0f%%",
			   lgl->stats->cce.count,
			   round,
			   tried,
			   lglpcnt(tried, count));
	if (elim2)
		lglprt(lgl,
			   1,
			   "[cce-%d-%d] eliminated %d binary clauses %.0f%%",
			   lgl->stats->cce.count,
			   round,
			   elim2,
			   lglpcnt(elim2, lgl->cce->bin));
	if (elim3)
		lglprt(lgl,
			   1,
			   "[cce-%d-%d] eliminated %d ternary clauses %.0f%%",
			   lgl->stats->cce.count,
			   round,
			   elim3,
			   lglpcnt(elim3, lgl->cce->trn));
	if (eliminated)
		lglprt(lgl,
			   1,
			   "[cce-%d-%d] eliminated %d small clauses %.0f%%",
			   lgl->stats->cce.count,
			   round,
			   eliminated,
			   lglpcnt(eliminated, count));
}

static int
lglcce(LGL* lgl)
{
	int oldvars = lgl->nvars, cce, lenlim, startirr, success;
	int oldirr, eliminated, total, idx, round;
	int completedsmall, completedlarge;
	int64_t oldsteps, deltasteps;
	int elmlarge, elmsmall;
	lglstart(lgl, &lgl->times->cce);
	lgl->stats->cce.count++;
	lglsetccelim(lgl);
	round = total = 0;
	startirr = lgl->stats->irr.clauses.cur;
RESTART:
	round++;
	lenlim = INT_MAX;
	cce = lgl->opts->cce.val;
	if (cce == 3 && lgl->ccertc < 2 && lgl->opts->cce3wait.val >= lgl->stats->cce.count) {
		lglprt(lgl,
			   2,
			   "[cce-%d-%d] restricted to ABCE since ccertc=%s",
			   lgl->stats->cce.count,
			   round,
			   lglcce2str(lgl->ccertc));
		cce = 2;
	}
	if (cce == 2 && lgl->ccertc < 1 && lgl->opts->cce2wait.val >= lgl->stats->cce.count) {
		lglprt(lgl,
			   2,
			   "[cce-%d-%d] restricted to ATE since ccertc=%s",
			   lgl->stats->cce.count,
			   round,
			   lglcce2str(lgl->ccertc));
		cce = 1;
	}
	if (cce > 1 && !(lgl->stats->cce.count % lgl->opts->cceateint.val)) {
		lglprt(lgl,
			   2,
			   "[cce-%d-%d] restricted to ATE due to ATE interval %d",
			   lgl->stats->cce.count,
			   round,
			   lgl->opts->cceateint.val);
		cce = 1;
	}
	oldirr = lgl->stats->irr.clauses.cur;
	lglprt(lgl, 2, "[cce-%d-%d] starting round %d (%s)", lgl->stats->cce.count, round, round, lglcce2str(cce));
	assert(!lgl->simp && !lgl->cceing && !lgl->occs);
	lgl->cceing = lgl->simp = 1;
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	NEW(lgl->cce, 1);
	NEW(lgl->cce->rem, oldvars);
	for (idx = 2; idx < oldvars; idx++)
		lgl->cce->rem[idx] = INT_MAX;
	lglgc(lgl);
	assert(lgl->frozen);
	lgldense(lgl, 1);
	oldsteps = lgl->stats->cce.steps;
	elmlarge = 0;
	while (!lgl->mt && lenlim > 4 && !lglterminate(lgl) && lgl->limits->cce.steps > lgl->stats->cce.steps) {
		int maxlen = 0, count;
		const int *p, *c;
		Stk lidcs;
		CLR(lidcs);
		for (c = lgl->irr.start; c < lgl->irr.top; c = p + 1) {
			int len, lidx;
			if (*(p = c) >= NOTALIT)
				continue;
			while (*p)
				p++;
			len = p - c;
			if (len >= lenlim)
				continue;
			if (len < maxlen)
				continue;
			if (len > maxlen) {
				lglclnstk(&lidcs);
				maxlen = len;
			}
			lidx = c - lgl->irr.start;
			lglpushstk(lgl, &lidcs, lidx);
		}
		ADDSTEPS(cce.steps, (int)lglcntstk(&lgl->irr) / 128);
		count = lglcntstk(&lidcs);
		lglprt(lgl, 2, "[cce-%d-%d] scheduling %d clauses of length %d", lgl->stats->cce.count, round, count, maxlen);
		eliminated = 0;
		for (p = lidcs.start;
			 p < lidcs.top && !lgl->mt && !lglterminate(lgl) && lgl->limits->cce.steps > lgl->stats->cce.steps;
			 p++) {
			int lidx = *p;
			c = lgl->irr.start + lidx;
			if (*c >= NOTALIT)
				continue;
			if (!lglcceclause(lgl, c, OCCS, cce))
				continue;
			lgldrupligdelclsaux(lgl, c);
			lglrmlcls(lgl, lidx, 0);
			eliminated++;
		}
		elmlarge += eliminated;
		lglrelstk(lgl, &lidcs);
		lenlim = maxlen;
		lglprt(lgl,
			   1 + !eliminated,
			   "[cce-%d-%d] eliminated %d clauses out of %d (%.0f%%) of length %d",
			   lgl->stats->cce.count,
			   round,
			   eliminated,
			   count,
			   lglpcnt(eliminated, count),
			   maxlen);
	}
	completedlarge = (lgl->limits->cce.steps > lgl->stats->cce.steps);
	if (completedlarge)
		lglprt(lgl, 1, "[cce-%d-%d] completed large round (%s)", lgl->stats->cce.count, round, lglcce2str(cce));
	else
		lglprt(lgl, 1, "[cce-%d-%d] incomplete large round (%s)", lgl->stats->cce.count, round, lglcce2str(cce));
	deltasteps = lgl->stats->cce.steps - oldsteps;
	lglprt(lgl,
		   1 + !elmlarge,
		   "[cce-%d-%d] eliminated %d large clauses in %lld steps",
		   lgl->stats->cce.count,
		   round,
		   elmlarge,
		   (LGLL)deltasteps);
	deltasteps /= 2;
	if (LLMAX - deltasteps > lgl->limits->cce.steps) {
		lgl->limits->cce.steps += deltasteps;
		lglprt(lgl,
			   1,
			   "[cce-%d-%d] allowing another %lld steps for small clauses",
			   lgl->stats->cce.count,
			   round,
			   deltasteps);
	} else {
		lgl->limits->cce.steps = LLMAX;
		lglprt(lgl, 1, "[cce-%d-%d] unlimited number of steps for small clauses", lgl->stats->cce.count, round);
	}
	lglccesmall(lgl, cce, round);
	elmsmall = oldirr - lgl->stats->irr.clauses.cur - elmlarge;
	lglprt(lgl,
		   1 + !elmsmall,
		   "[cce-%d-%d] eliminated %d small clauses in %lld steps",
		   lgl->stats->cce.count,
		   round,
		   elmsmall,
		   (LGLL)lgl->stats->cce.steps - oldsteps - deltasteps);
	completedsmall = (lgl->limits->cce.steps > lgl->stats->cce.steps);
	COVER(!completedsmall && lgl->opts->ccertc.val > 1);
	if (completedsmall)
		lglprt(lgl, 1, "[cce-%d-%d] completed small round (%s)", lgl->stats->cce.count, round, lglcce2str(cce));
	else
		lglprt(lgl, 1, "[cce-%d-%d] incomplete small round (%s)", lgl->stats->cce.count, round, lglcce2str(cce));
	if (completedsmall && completedlarge) {
		if (lgl->ccertc < cce) {
			lgl->ccertc = cce;
			lglprt(lgl, 1, "[cce-%d-%d] completed small and large (%s)", lgl->stats->cce.count, round, lglcce2str(cce));
		}
	}
	lglsparse(lgl);
	lglgc(lgl);
	lglrelstk(lgl, &lgl->cce->extend);
	lglrelstk(lgl, &lgl->cce->cla);
	DEL(lgl->cce->rem, oldvars);
	DEL(lgl->cce, 1);
	assert(oldirr >= lgl->stats->irr.clauses.cur);
	eliminated = oldirr - lgl->stats->irr.clauses.cur;
	total += eliminated;
	lglprt(lgl,
		   1,
		   "[cce-%d-%d] eliminated %d covered clauses in round %d",
		   lgl->stats->cce.count,
		   round,
		   eliminated,
		   round);
	assert(lgl->simp && lgl->cceing);
	lgl->cceing = lgl->simp = 0;
	if (!lgl->mt && eliminated && !lglterminate(lgl) && round < lgl->opts->ccemaxround.val &&
		lgl->limits->cce.steps > lgl->stats->cce.steps)
		goto RESTART;
	lglprt(lgl,
		   1,
		   "[cce-%d] eliminated %d covered clauses in TOTAL during %d rounds",
		   lgl->stats->cce.count,
		   total,
		   round);
	if (total) {
		success = (startirr / lgl->opts->ccesuccessrat.val <= total);
		if (!success)
			lglprt(lgl,
				   1,
				   "[cce-%d] %d < 1/%d * %d = %d considered unsuccessful",
				   lgl->stats->cce.count,
				   total,
				   lgl->opts->ccesuccessrat.val,
				   startirr,
				   startirr / lgl->opts->ccesuccessrat.val);
	} else
		success = 0;
	LGLUPDPEN(cce, success);
	lglrep(lgl, 2, 'E');
	lglstop(lgl);
	lglbasicatestats(lgl);
	return !lgl->mt;
}

static void
lglsetelmlim(LGL* lgl, int* reschedptr)
{
	int count = lgl->stats->elm.count - lgl->opts->elmboostdel.val;
	int pen, szpen, resched = 0;
	int64_t limit, irrlim;
	if (lgl->opts->elmrtc.val > 1) {
		lgl->limits->elm.steps = LLMAX;
		lglprt(lgl, 1, "[elim-%d] really no limit (run to completion)", lgl->stats->elm.count);
		resched = (lgl->opts->elmresched.val & 4);
	} else if (lgl->opts->elmrtc.val ||
			   (count > 0 && lglrem(lgl) < lgl->opts->elmrtcintvlim.val && !(count % lgl->opts->elmrtcint.val))) {
		limit = 100000000000ll;
		lgl->limits->elm.steps = lgl->stats->elm.steps + limit;
		lglprt(lgl, 1, "[elim-%d] almost no limit of %lld steps", lgl->stats->elm.count, (LGLL)limit);
		resched = (lgl->opts->elmresched.val & 4);
	} else {
		limit = (lgl->opts->elmreleff.val * lglvisearch(lgl)) / 1000;
		if (limit < lgl->opts->elmineff.val)
			limit = lgl->opts->elmineff.val;
		if (lgl->opts->elmaxeff.val >= 0 && limit > lgl->opts->elmaxeff.val)
			limit = lgl->opts->elmaxeff.val;
		if (count > 0 && (count == 1 || !(count % lgl->opts->elmboostint.val)) &&
			lglrem(lgl) < lgl->opts->elmboostvlim.val && lgl->opts->boost.val && lgl->opts->elmboost.val > 1) {
			lglprt(lgl, 1, "[elim-%d] boosting limit by %d", lgl->stats->elm.count, lgl->opts->elmboost.val);
			limit *= lgl->opts->elmboost.val;
			resched = (lgl->opts->elmresched.val & 2);
		} else
			resched = (lgl->opts->elmresched.val & 1);
		limit >>= (pen = lgl->limits->elm.pen + (szpen = lglszpen(lgl)));
		irrlim = (lgl->stats->irr.clauses.cur) >> szpen;
		if (lgl->opts->irrlim.val && limit < irrlim) {
			limit = irrlim;
			lglprt(lgl,
				   1,
				   "[elim-%d] limit of %lld steps based on %d irredundant clauses penalty %d",
				   lgl->stats->elm.count,
				   (LGLL)limit,
				   lgl->stats->irr.clauses.cur,
				   szpen);
		} else
			lglprt(lgl,
				   1,
				   "[elim-%d] limit of %lld steps penalty %d = %d + %d",
				   lgl->stats->elm.count,
				   (LGLL)limit,
				   pen,
				   lgl->limits->elm.pen,
				   szpen);
		lgl->limits->elm.steps = lgl->stats->elm.steps + limit;
	}
	lglprt(lgl,
		   1, // TODO 2?
		   "[elim-%d] rescheduling of touched variables %s",
		   lgl->stats->elm.count,
		   resched ? "enabled" : "disabled");
	*reschedptr = resched;
}

static int
lglforceschedall(LGL* lgl)
{
	const int oldonotsched = lgl->donotsched;
	int idx, res = 0;
	AVar* av;
	assert(lgl->eliminating);
	assert(lglmtstk(&lgl->esched));
	assert(lgl->touching), lgl->touching = 0;
	lgl->donotsched = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (lglifrozen(lgl, idx))
			continue;
		if (!lglisfree(lgl, idx))
			continue;
		av = lglavar(lgl, idx);
		av->donotelm = 0;
		lglesched(lgl, idx);
		res++;
	}
	assert(!lgl->touching), lgl->touching = 1;
	assert(!lgl->donotsched);
	if (oldonotsched)
		lgl->donotsched = 1;
	lglprt(lgl,
		   1,
		   "[elim-%d-%d] fully rescheduled %d variables %.0f%%",
		   lgl->stats->elm.count,
		   lgl->elm->round,
		   res,
		   lglpcnt(res, lgl->nvars - 2));
	return res;
}

static void
lgladdstrbincls(LGL* lgl, int a, int b)
{
	assert(lglisfree(lgl, a));
	assert(lglisfree(lgl, b));
	assert(lglmtstk(&lgl->clause));
	lglpushstk(lgl, &lgl->clause, a);
	lglpushstk(lgl, &lgl->clause, b);
	lglpushstk(lgl, &lgl->clause, 0);
	LOG(BWL, "strengthened binary clause", a, b);
	lgldrupligaddcls(lgl, REDCS);
	lgladdcls(lgl, 0, 0, 1);
	lglclnstk(&lgl->clause);
}

static int
lglbackwardlit(LGL* lgl, const int* clause, const int* skip, int size, int minlit, int* subptr, int* strptr)
{
	int res, blit, tag, red, other, other2, lidx, count, remove, hit;
	const int druplig = lgl->opts->druplig.val;
	const int *p, *w, *eow, *c, *l;
	int marked, marked2, val;
	Stk saved;
	HTS* hts;
	CLR(saved);
	assert(!lgl->level);
	lgl->stats->bkwd.tried.lits++;
	LOGCLS(BWL, clause, "backward subsume and strengthen clauses with %d by size %d clause", minlit, size);
	hit = res = 0;
	hts = lglhts(lgl, minlit);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; !res && p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == LRGCS)
			continue;
		if (p == skip)
			continue;
		red = blit & REDCS;
		if (red)
			continue;
		lgl->stats->bkwd.tried.occs++;
		INCSTEPS(elm.steps);
		if (tag == BINCS) {
			if (size > 2)
				continue;
			other = blit >> RMSHFT;
			if (!lglisfree(lgl, other))
				continue;
			marked = lglmarked(lgl, other);
			if (marked > 0) {
				if (size == 2 && !hit++)
					continue;
				LOG(BWL, "subsumed binary clause %d %d", minlit, other);
				assert(!(w <= skip && skip < eow) || skip < p);
				ADDSTEPS(elm.steps, 2);
				if (druplig)
					lgldrupligdelclsarg(lgl, minlit, other, 0);
				lglrmbcls(lgl, minlit, other, 0);
				lgl->stats->bkwd.sub2++;
				*subptr += 1;
				res = 1;
			} else if (marked < 0) {
				LOG(BWL, "removing %d and thus strengthening binary clause %d %d", other, minlit, other);
				assert(p != skip);
				ADDSTEPS(elm.steps, 2);
				lglunit(lgl, minlit);
				lgl->stats->bkwd.str2++;
				*strptr += 1;
				res = 1;
			}
		} else if (tag == TRNCS) {
			if (size > 3)
				continue;
			other = blit >> RMSHFT;
			other2 = *p;
			if (!lglisfree(lgl, other))
				continue;
			if (!lglisfree(lgl, other2))
				continue;
			marked = lglmarked(lgl, other);
			marked2 = lglmarked(lgl, other2);
			if ((size == 2 && (marked > 0 || marked2 > 0)) || (size == 3 && marked > 0 && marked2 > 0)) {
				if (size == 3 && !hit++)
					continue;
				assert(!(w <= skip && skip < eow) || skip < p);
				LOG(BWL, "subsumed ternary clause %d %d %d", minlit, other, other2);
				ADDSTEPS(elm.steps, 3);
				if (druplig)
					lgldrupligdelclsarg(lgl, minlit, other, other2, 0);
				lglrmtcls(lgl, minlit, other, other2, 0);
				lgl->stats->bkwd.sub3++;
				*subptr += 1;
				res = 1;
			} else {
				if (marked < 0 && marked2 > 0) {
					int tmp = other;
					other = other2;
					other2 = tmp;
				} else if (marked <= 0 || marked2 >= 0)
					continue;
				LOG(BWL, "removing %d and thus strengthening ternary clause %d %d %d", other2, minlit, other, other2);
				assert(lglmarked(lgl, other) > 0);
				assert(lglmarked(lgl, other2) < 0);
				lgladdstrbincls(lgl, minlit, other);
				if (druplig)
					lgldrupligdelclsarg(lgl, minlit, other, other2, 0);
				lglrmtcls(lgl, minlit, other, other2, 0);
				if (size == 3) {
					LOG(BWL,
						"removing %d and thus strengthening ternary clause %d %d %d",
						other2,
						minlit,
						other,
						-other2);
					if (druplig)
						lgldrupligdelclsarg(lgl, minlit, other, -other2, 0);
					lglrmtcls(lgl, minlit, other, -other2, 0);
					lgl->stats->bkwd.str3self++;
				}
				ADDSTEPS(elm.steps, 3);
				lgl->stats->bkwd.str3++;
				*strptr += 1;
				res = 1;
			}
		} else {
			assert(tag == OCCS);
			lidx = blit >> RMSHFT;
			c = lglidx2lits(lgl, 0, lidx);
			if (c == skip)
				continue;
			INCSTEPS(elm.steps);
			count = size;
			remove = 0;
			for (l = c; (other = *l); l++) {
				val = lglval(lgl, other);
				if (val > 0) {
					remove = INT_MAX;
					break;
				}
				if (val < 0)
					continue;
				assert(!val);
				marked = lglmarked(lgl, other);
				if (!marked)
					continue;
				count--;
				if (marked > 0)
					continue;
				if (remove) {
					remove = INT_MAX;
					break;
				}
				remove = other;
			}
			if (count > 0)
				continue;
			if (remove == INT_MAX)
				continue;
			if (!remove) {
				assert(!(w <= skip && skip < eow) || skip < p);
				LOGCLS(BWL, c, "subsumed large clause");
				ADDSTEPS(elm.steps, (l - c));
				if (druplig)
					lgldrupligdelclsaux(lgl, c);
				lglrmlcls(lgl, lidx, 0);
				lgl->stats->bkwd.subl++;
				*subptr += 1;
				res = 1;
			} else if (lglsmallirr(lgl)) {
				LOGCLS(BWL, c, "removing %d and thus strengthening clause", remove);
				assert(lglmtstk(&lgl->clause));
				assert(lglmtstk(&saved));
				for (l = c; (other = *l); l++) {
					if (druplig)
						lglpushstk(lgl, &saved, other);
					if (other == remove)
						continue;
					val = lglval(lgl, other);
					if (val < 0)
						continue;
					assert(!val);
					lglpushstk(lgl, &lgl->clause, other);
				}
				lglpushstk(lgl, &lgl->clause, 0);
				LOGCLS(BWL, lgl->clause.start, "strengthened clause");

				if (druplig) {
					lgldrupligaddcls(lgl, REDCS);
					assert(l - c == (int)lglcntstk(&saved));
					lglpushstk(lgl, &saved, 0);
					lgldrupligdelclsaux(lgl, saved.start);
					lglclnstk(&saved);
				}
				ADDSTEPS(elm.steps, (l - c));
				lglrmlcls(lgl, lidx, 0);
				if (l - c == size)
					lgl->stats->bkwd.strlself++;

				lgladdcls(lgl, 0, 0, 1);
				lglclnstk(&lgl->clause);
				lgl->stats->bkwd.strl++;
				*strptr += 1;
				res = 1;
			}
		}
	}
	lglrelstk(lgl, &saved);
	(void)clause;
	return res;
}

static int
lglbackwardclause(LGL* lgl, const int* clause, const int* skip, int* subsumedptr, int* strengthenedptr)
{
	int lit, size, minlit, minlit2, minocc, minocc2, tmpocc, res, large;
	const int* p;
	long delta;
	lgl->stats->bkwd.tried.clauses++;
	INCSTEPS(elm.steps);
	minocc = minlit = minocc2 = minlit2 = 0;
	for (p = clause; (lit = *p); p++) {
		lglmarkunmarked(lgl, lit);
		tmpocc = lglocc(lgl, lit);
		if (!minlit || tmpocc < minocc) {
			minocc2 = minocc, minlit2 = minlit;
			minocc = tmpocc, minlit = lit;
		} else if (!minlit2 || tmpocc < minocc2) {
			minocc2 = tmpocc, minlit2 = lit;
		}
	}
	size = p - clause;
	assert(size >= 2);
	LOG(BWL, "minimum occurrence %d of literal %d", minocc, minlit);
	LOG(BWL, "next minimum occurrence %d of literal %d", minocc2, minlit2);
	large = (lgl->irr.start <= clause && clause < lgl->irr.top);
	delta = large ? clause - lgl->irr.start : 0;
	res = lglbackwardlit(lgl, clause, skip, size, minlit, subsumedptr, strengthenedptr);
	if (!res) {
		res = lglbackwardlit(lgl, clause, skip, size, minlit2, subsumedptr, strengthenedptr);
	}
	if (res && large)
		clause = lgl->irr.start + delta;
	for (p = clause; (lit = *p); p++)
		lglunmark(lgl, lit);
	return res;
}

static void
lglbackward(LGL* lgl, int* u, int* t, int64_t steps, int* completedptr)
{
	int idx, sign, lit, blit, red, tag, other, other2, clause[4], *w;
	int oldtouched, newtouched, round, touched;
	int64_t limit, delta, scaled;
	const int *p, *eow, *c;
	HTS* hts;
	Stk stk;
	lglstart(lgl, &lgl->times->backward);
	lgl->elm->bkwdocclim = lglfactor(lgl, lgl->opts->bkwdocclim.val, lgl->stats->elm.count);
	CLR(stk);
	*u = *t = 0;
	if (LLMAX / lgl->opts->bkwdscale.val <= steps / 100)
		scaled = LLMAX;
	else
		scaled = steps / 100 * lgl->opts->bkwdscale.val;
	if (lgl->limits->elm.steps - scaled <= lgl->stats->elm.steps)
		limit = lgl->limits->elm.steps;
	else
		limit = lgl->stats->elm.steps + scaled;
	delta = limit - lgl->stats->elm.steps;
	lglprt(lgl,
		   1,
		   "[elim-%d-%d] backward subsumption/strengthening limit %lld",
		   lgl->stats->elm.count,
		   lgl->elm->round,
		   delta);
	round = 0;
RESTARTOUCHED:
	round++;
	oldtouched = lglcntstk(&lgl->elm->touched.stk);
	assert(oldtouched >= lgl->elm->touched.mt);
	oldtouched -= lgl->elm->touched.mt;
	lglprt(lgl,
		   1,
		   "[elim-%d-%d-%d] backward checking %d touched variables %.0f%%",
		   lgl->stats->elm.count,
		   lgl->elm->round,
		   round,
		   oldtouched,
		   lglpcnt(oldtouched, lglrem(lgl)));
	*completedptr = 0;
	while ((idx = lglpoptouched(lgl))) {
		for (sign = -1; sign <= 1; sign += 2) {
		RESTARTLIT:
			if (lgl->mt)
				goto DONE;
			if (lglterminate(lgl))
				goto DONE;

			// TODO DID NOT WORK?  REMOVE OR FIX?
			// if (!lglsyncunits (lgl)) goto DONE;

			if (!lglisfree(lgl, idx))
				continue;
			if (INCSTEPS(elm.steps) >= limit)
				goto DONE;
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			lglclnstk(&stk);
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (tag != BINCS)
					continue;
				red = blit & REDCS;
				if (red)
					continue;
				lglpushstk(lgl, &stk, blit);
			}
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (tag != TRNCS)
					continue;
				red = blit & REDCS;
				if (red)
					continue;
				lglpushstk(lgl, &stk, blit);
				lglpushstk(lgl, &stk, *p);
			}
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				red = blit & REDCS;
				if (!red && (tag == BINCS || tag == TRNCS))
					continue;
				lglpushstk(lgl, &stk, blit);
				if (tag == TRNCS || tag == LRGCS)
					lglpushstk(lgl, &stk, *p);
			}
			assert(lglcntstk(&stk) == hts->count);
			memcpy(w, stk.start, lglcntstk(&stk) * sizeof *w);
			clause[0] = lit;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (tag == LRGCS)
					continue;
				red = blit & REDCS;
				if (red)
					continue;
				if (INCSTEPS(elm.steps) >= limit)
					goto DONE;
				if (tag == BINCS) {
					other = blit >> RMSHFT;
					if (abs(other) < idx)
						continue;
					clause[1] = other, clause[2] = 0;
					if (!lglbackwardclause(lgl, clause, p, u, t))
						continue;
				} else if (tag == TRNCS) {
					other = blit >> RMSHFT;
					if (abs(other) < idx)
						continue;
					other2 = *p;
					if (abs(other2) < idx)
						continue;
					clause[1] = other, clause[2] = other2, clause[3] = 0;
					if (!lglbackwardclause(lgl, clause, p, u, t))
						continue;
				} else {
					assert(tag == OCCS);
					continue;
				}
				if (lglflush(lgl))
					goto RESTARTLIT;
				else
					goto DONE;
			}
		}
	}
	for (c = lgl->irr.start; c < lgl->irr.top; c = p + 1) {
		if (!lglsmallirr(lgl))
			goto DONE;
		if (INCSTEPS(elm.steps) >= limit)
			goto DONE;
		if (*(p = c) >= NOTALIT)
			continue;
		touched = 0;
		while ((lit = *p)) {
			if (!touched && lgltouched(lgl, lit))
				touched = 1;
			p++;
		}
		if (touched)
			lglbackwardclause(lgl, c, c, u, t);
	}
	if (round < lgl->opts->bkwdroundlim.val && !lglmtstk(&lgl->elm->touched.stk))
		goto RESTARTOUCHED;
	*completedptr = 1;
DONE:
	newtouched = lglcntstk(&lgl->elm->touched.stk);
	assert(newtouched >= lgl->elm->touched.mt);
	newtouched -= lgl->elm->touched.mt;
	if (*completedptr)
		assert(!newtouched), lglprt(lgl,
									1,
									"[elim-%d-%d-%d] fully completed backward checking in %d rounds",
									lgl->stats->elm.count,
									lgl->elm->round,
									round,
									round);
	else
		lglprt(lgl,
			   1,
			   "[elim-%d-%d-%d] incomplete backward checking %d remain %.0f%%",
			   lgl->stats->elm.count,
			   lgl->elm->round,
			   round,
			   newtouched,
			   lglpcnt(newtouched, oldtouched));
	lglrelstk(lgl, &stk);
	lglstop(lgl);
}

static int
lglelmdone(LGL* lgl, int* allptr)
{
	int newelmd, eliminated, subsumed, strengthened, bkwdcompleted;
	const int oldonotsched = lgl->donotsched;
	const int newdonotsched = !lgl->opts->bkwdresched.val;
	int64_t steps, oldsteps;
	if (!lglsmallirr(lgl))
		return 1;
	if (lglterminate(lgl))
		return 1;
	if (lgl->limits->elm.steps <= lgl->stats->elm.steps)
		return 1;
	if (!lglmtstk(&lgl->esched))
		return 0;
	steps = ((oldsteps = lgl->stats->elm.steps) - lgl->elm->oldsteps);
	assert(steps >= 0);
	eliminated = (newelmd = lgl->stats->elm.elmd) - lgl->elm->oldelmd;
	assert(eliminated >= 0);
	if (eliminated <= 0) {
		lglprt(lgl,
			   1,
			   "[elim-%d-%d] no variable eliminated in round %d in %lld steps",
			   lgl->stats->elm.count,
			   lgl->elm->round,
			   lgl->elm->round,
			   (LGLL)steps);
		return 1;
	}
	lglprt(lgl,
		   1,
		   "[elim-%d-%d] eliminated %d variables %.0f%% in round %d in %lld steps",
		   lgl->stats->elm.count,
		   lgl->elm->round,
		   eliminated,
		   lglpcnt(eliminated, lgl->nvars - 2),
		   lgl->elm->round,
		   (LGLL)steps);
	if (oldonotsched != newdonotsched)
		lgl->donotsched = newdonotsched;
	lglbackward(lgl, &subsumed, &strengthened, steps, &bkwdcompleted);
	if (oldonotsched != newdonotsched)
		lgl->donotsched = oldonotsched;
	steps = lgl->stats->elm.steps - oldsteps;
	lglprt(lgl,
		   1,
		   "[elim-%d-%d] subsumed %d and strengthened %d clauses in %lld steps",
		   lgl->stats->elm.count,
		   lgl->elm->round,
		   subsumed,
		   strengthened,
		   (LGLL)steps);
	lgl->stats->elm.rounds++;
	lgl->elm->oldelmd = newelmd;
	lgl->elm->oldsteps = lgl->stats->elm.steps;
	if (lgl->mt)
		return 1;
	if (lgl->limits->elm.steps <= lgl->stats->elm.steps)
		return 1;
	if (lgl->elm->round >= lgl->opts->elmroundlim.val)
		return 1;
	if (!lglmtstk(&lgl->esched)) {
		lglprt(lgl,
			   1,
			   "[elim-%d-%d] rescheduled %d variables %.0f%% by backward subsumption",
			   lgl->stats->elm.count,
			   lgl->elm->round,
			   lglcntstk(&lgl->esched),
			   lglpcnt(lglcntstk(&lgl->esched), lglrem(lgl)));
		lgl->elm->round++;
		return 0;
	}
	if (bkwdcompleted && *allptr)
		return 1;
	*allptr = 1;
	if (!lglforceschedall(lgl))
		return 1;
	lgl->elm->round++;
	return 0;
}

static void
lglinitouched(LGL* lgl)
{
	int idx;
	assert(lgl->eliminating);
	lgl->elm->touched.nvars = lgl->nvars;
	NEW(lgl->elm->touched.pos, lgl->nvars);
	for (idx = 2; idx < lgl->nvars; idx++)
		lgl->elm->touched.pos[idx] = -1;
}

static void
lglreltouched(LGL* lgl)
{
	lglrelstk(lgl, &lgl->elm->touched.stk);
	DEL(lgl->elm->touched.pos, lgl->elm->touched.nvars);
}

static int
lglelim(LGL* lgl)
{
	int res = 1, idx, elmd, oldnvars, sched, success, all, rem;
	int oldrem = lgl->elmrem, oldall = lgl->elmall, resched;
	assert(lgl->opts->elim.val);
	assert(!lgl->mt);
	assert(lgl->nvars > 2);
	assert(!lgl->eliminating);
	assert(!lgl->simp);
	assert(!lgl->occs);
	lglstart(lgl, &lgl->times->elim);
	lgl->stats->elm.count++;
	lgl->eliminating = lgl->simp = lgl->occs = 1;
	NEW(lgl->elm, 1);
	lgl->elm->oldelmd = lgl->stats->elm.elmd;
	lgl->elm->round = 1;
	lgl->elm->oldsteps = lgl->stats->elm.steps;
	lgl->stats->elm.rounds++;
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	oldnvars = lglrem(lgl);
	lglgc(lgl);
	assert(lgl->frozen);
	assert(!(oldall && !oldrem));
	all = !oldrem || !oldall;
	if (all)
		lglprt(lgl, 1, "[elim-%d] scheduling all variables this time", lgl->stats->elm.count);
	else if (!lgleschedrem(lgl, 1))
		all = 1, oldrem = 0;
	if (!all)
		assert(!lgl->donotsched), lgl->donotsched = 1;
	assert(!lgl->touching);
	lgldense(lgl, 1);
	lglinitouched(lgl);
	if (!all)
		assert(lgl->donotsched), lgl->donotsched = 0;
	assert(!lgl->touching), lgl->touching = 1;
	lglsetelmlim(lgl, &resched);
	if (!resched)
		assert(!lgl->donotsched), lgl->donotsched = 1;
	sched = lglcntstk(&lgl->esched);
	while (res && !lglelmdone(lgl, &all)) {
		idx = lglpopesched(lgl);
		lglavar(lgl, idx)->donotelm = 1;
		lglelimlit(lgl, idx);
		res = lglflush(lgl);
		assert(res || lgl->mt);
	}
	assert(lgl->touching), lgl->touching = 0;
	if (!resched)
		assert(lgl->donotsched), lgl->donotsched = 0;
	rem = lglcntstk(&lgl->esched);
	if (!rem) {
		lglprt(lgl, 1, "[elim-%d] fully completed in %d rounds", lgl->stats->elm.count, lgl->elm->round);
		lgl->elmrtc = 1;
	} else {
		lglprt(lgl,
			   1,
			   "[elim-%d] incomplete %d not tried %.0f%% in round %d",
			   lgl->stats->elm.count,
			   rem,
			   lglpcnt(rem, lgl->nvars - 2),
			   lgl->elm->round);
	}
	lglsetdonotesched(lgl, !rem);
	lglrelstk(lgl, &lgl->esched);
	lglreltouched(lgl);
	lglrelecls(lgl);
	lglsparse(lgl);
	lglgc(lgl);
	DEL(lgl->elm, 1);
	lgl->elmrem = rem > 0;
	lgl->elmall = all && lgl->elmrem;
	lglprt(lgl, 1, "[elim-%d] transition to [ all %d rem %d ] state", lgl->stats->elm.count, lgl->elmall, lgl->elmrem);
	elmd = oldnvars - lglrem(lgl);
	lgl->stats->irrprgss += elmd;
	lglprt(lgl,
		   1,
		   "[elim-%d] eliminated %d = %.0f%% variables out of %d scheduled",
		   lgl->stats->elm.count,
		   elmd,
		   lglpcnt(elmd, sched),
		   sched);
	if (!lgl->elmrtc && lgl->stats->elm.count <= lgl->opts->elmsuccessmaxwortc.val) {
		success = 1;
		lglprt(lgl, 1, "[elim-%d] considered successful since not run to completion yet", lgl->stats->elm.count);
	} else if (elmd) {
		success = (oldnvars / lgl->opts->elmsuccessrat.val <= elmd);
		if (!success)
			lglprt(lgl,
				   1,
				   "[elim-%d] %d < 1/%d * %d = %d considered unsuccessful",
				   lgl->stats->elm.count,
				   elmd,
				   lgl->opts->elmsuccessrat.val,
				   sched,
				   sched / lgl->opts->elmsuccessrat.val);
	} else
		success = 0;
	LGLUPDPEN(elm, success);
	lglrep(lgl, 2, 'e');
	assert(lgl->eliminating && lgl->simp && lgl->occs);
	lgl->eliminating = lgl->simp = lgl->occs = 0;
	lglstop(lgl);
	return !lgl->mt;
}

static int
lglsynceqs(LGL* lgl)
{
	int *ereprs, emax = lgl->maxext;
	int elit1, erepr1, elit2, erepr2;
	int ilit1, irepr1, ilit2, irepr2;
	int consumed = 0, produced = 0;
	assert(!lgl->mt);
	assert(!lgl->level);
	if (!lgl->nvars)
		return 1;
	if (!lgl->cbs)
		return 1;
	if (!lgl->cbs->eqs.lock.fun)
		return 1;
	assert(lgl->repr);
	ereprs = lgl->cbs->eqs.lock.fun(lgl->cbs->eqs.lock.state);
	produced = consumed = 0;
	for (elit1 = 1; elit1 <= emax; elit1++) {
		if (lglelitblockingoreliminated(lgl, elit1))
			continue;
		elit2 = lglptrjmp(ereprs, emax, elit1);
		if (elit2 == elit1)
			continue;
		if (lglelitblockingoreliminated(lgl, elit2))
			continue;
		assert(elit2 != -elit1);
		erepr1 = lglerepr(lgl, elit1);
		if (lglelitblockingoreliminated(lgl, erepr1))
			continue;
		erepr2 = lglerepr(lgl, elit2);
		if (lglelitblockingoreliminated(lgl, erepr2))
			continue;
		if (erepr1 == erepr2)
			continue;
		if (erepr1 == -erepr2) {
		INCONSISTENT:
			LOG(1, "inconsistent external equivalence %d %d", elit1, elit2);
			assert(!lgl->level);
			lglmt(lgl);
			goto DONE;
		}
		ilit1 = lglimport(lgl, elit1);
		ilit2 = lglimport(lgl, elit2);
		if (ilit1 == ilit2)
			continue;
		if (ilit1 == -ilit2)
			goto INCONSISTENT;
		if (abs(ilit1) <= 1)
			continue;
		if (abs(ilit2) <= 1)
			continue;
		irepr1 = lglirepr(lgl, ilit1);
		irepr2 = lglirepr(lgl, ilit2);
		if (irepr1 == irepr2)
			continue;
		if (irepr1 == -irepr2)
			goto INCONSISTENT;
		if (abs(irepr1) <= 1)
			continue;
		if (abs(irepr2) <= 1)
			continue;
		LOG(2, "importing external equivalence %d %d as internal %d %d", elit1, elit2, irepr1, irepr2);
		if (!lglisfree(lgl, irepr1))
			continue;
		if (!lglisfree(lgl, irepr2))
			continue;
		consumed++;
		lglimerge(lgl, irepr1, irepr2);
	}
	LOG(1, "consumed %d equivalences", consumed);
	for (elit1 = 1; elit1 <= emax; elit1++) {
		elit2 = lglerepr(lgl, elit1);
		if (elit1 == elit2)
			continue;
		assert(elit1 != -elit2);
		erepr1 = lglptrjmp(ereprs, emax, elit1);
		erepr2 = lglptrjmp(ereprs, emax, elit2);
		if (erepr1 == erepr2)
			continue;
		assert(erepr1 != -erepr2);
		LOG(2, "exporting external equivalence %d %d", erepr1, erepr2);
		produced++;
		ereprs[abs(erepr1)] = (erepr1 < 0) ? -erepr2 : erepr2;
	}
	LOG(1, "produced %d equivalences", produced);
DONE:
	if (lgl->cbs->eqs.unlock.fun)
		lgl->cbs->eqs.unlock.fun(lgl->cbs->eqs.unlock.state, consumed, produced);
	return !lgl->mt;
}

static int
lgldecomp(LGL* lgl)
{
	int res = 1, oldnvars = lgl->nvars, removed;
	assert(lgl->opts->decompose.val || lgl->probing || lgl->gaussing || lgl->sweeping);
	assert(!lgl->decomposing);
	lglstart(lgl, &lgl->times->decompose);
	lgl->stats->decomps++;
	lgl->decomposing = 1;
	lgl->simp++;
	assert(lgl->simp > 0);
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	res = 0;
	lglgc(lgl);
	if (!lglsyncunits(lgl))
		goto DONE;
	lglgc(lgl);
	if (lgl->mt)
		goto DONE;
	if (!lgltarjan(lgl))
		goto DONE;
	if (!lglsynceqs(lgl))
		goto DONE;
	lglchkred(lgl);
	lgldcpdis(lgl);
	lgldcpcln(lgl);
	lgldcpcon(lgl);
	lglcompact(lgl);
	lglmap(lgl);
	if (lgl->mt)
		goto DONE;
	if (!lglbcp(lgl)) {
		lglmt(lgl);
		goto DONE;
	}
	lglcount(lgl);
	lglgc(lgl);
	if (lgl->mt)
		goto DONE;
	res = 1;
DONE:
	if (lgl->repr)
		DEL(lgl->repr, lgl->nvars);
	assert(lgl->decomposing);
	lgl->decomposing = 0;
	ASSERT(lgl->simp > 0);
	lgl->simp--;
	removed = oldnvars - lgl->nvars;
	if (removed)
		lglprt(lgl, 1, "[decomp-%d] removed %d variables", lgl->stats->decomps, removed);
	lglrep(lgl, 2, 'd');
	lglstop(lgl);
	return res;
}

int
lglnvars(LGL* lgl)
{
	return lglrem(lgl);
}

int
lglnclauses(LGL* lgl)
{
	return lgl->stats->irr.clauses.cur;
}

static int
lgldstpull(LGL* lgl, int lit)
{
	AVar* av;
	av = lglavar(lgl, lit);
	assert((lit > 0) == av->wasfalse);
	if (av->mark)
		return 0;
	if (!lglevel(lgl, lit))
		return 0;
	av->mark = 1;
	if (lgldecision(lgl, lit)) {
		lglpushstk(lgl, &lgl->clause, lit);
		LOG(3, "added %d to learned clause", lit);
	} else {
		lglpushstk(lgl, &lgl->seen, -lit);
		LOG(3, "pulled in distillation literal %d", -lit);
	}
	return 1;
}

static int
lglanalit(LGL* lgl, int lit)
{
	int r0, r1, antecedents, other, next, tag, *p, *rsn;
	AVar* av;
	assert(lglmtstk(&lgl->seen));
	assert(lglmtstk(&lgl->clause));
	antecedents = 1;
	av = lglavar(lgl, lit);
	rsn = lglrsn(lgl, lit);
	r0 = rsn[0], r1 = rsn[1];
	LOGREASON(2, lit, r0, r1, "starting literal analysis for %d with", lit);
	LOG(3, "added %d to learned clause", lit);
	lglpushstk(lgl, &lgl->clause, lit);
	assert((lit < 0) == av->wasfalse);
	assert(!av->mark);
	av->mark = 1;
	next = 0;
	for (;;) {
		tag = r0 & MASKCS;
		if (tag == BINCS || tag == TRNCS) {
			other = r0 >> RMSHFT;
			lgldstpull(lgl, other);
			if (tag == TRNCS)
				lgldstpull(lgl, r1);
		} else if (tag == UNITCS)
			assert(!lglevel(lgl, lit));
		else if (tag == DECISION)
			assert(lglavar(lgl, lit)->assumed);
		else {
			assert(tag == LRGCS);
			for (p = lglidx2lits(lgl, (r0 & REDCS), r1); (other = *p); p++)
				if (other != lit)
					lgldstpull(lgl, *p);
		}
		if (next == (int)lglcntstk(&lgl->seen))
			break;
		lit = lglpeek(&lgl->seen, next++);
		assert((lit < 0) == lglavar(lgl, lit)->wasfalse);
		rsn = lglrsn(lgl, lit);
		r0 = rsn[0], r1 = rsn[1];
		LOGREASON(2, lit, r0, r1, "literal analysis of");
		antecedents++;
	}
	lglpopnunmarkstk(lgl, &lgl->seen);
	LOG(2, "literal analysis used %d antecedents", antecedents);
	assert(lglcntstk(&lgl->clause) >= 1);
	return antecedents;
}

static int
lglfailedass(LGL* lgl)
{
	assert(lgl->level >= lgl->alevel);
	return lgl->level == lgl->alevel && lgl->failed;
}

static void
lglanafailed(LGL* lgl)
{
	int ilit, elit, erepr, failed, size;
	unsigned bit, rbit, ibit, count;
	Ext *ext, *rext;
	const int* p;
	AVar* av;
	assert(lgl->mt || lglfailedass(lgl));
	if (lgl->mt) {
		LOG(1, "no failed assumptions since CNF unconditionally inconsistent");
	} else if ((failed = lgl->failed) == -1) {
		assert(!lgl->level);
		elit = 0;
		for (p = lgl->eassume.start; !elit && p < lgl->eassume.top; p++) {
			erepr = lglerepr(lgl, *p);
			if (lglederef(lgl, erepr) < 0)
				elit = *p;
		}
		assert(elit);
		LOG(1, "found single external failed assumption %d", elit);
		ext = lglelit2ext(lgl, elit);
		assert(!ext->failed);
		bit = 1u << (elit < 0);
		assert(ext->assumed & bit);
		ext->failed |= bit;
	} else {
		assert(abs(failed) > 1);
		if ((av = lglavar(lgl, failed))->assumed == 3) {
			LOG(1, "inconsistent internal assumptions %d and %d", failed, -failed);
			assert(!av->failed);
			av->failed = 3;
		} else {
			lglanalit(lgl, -failed);
			for (p = lgl->clause.start; p < lgl->clause.top; p++) {
				ilit = *p;
				av = lglavar(lgl, ilit);
				bit = (1u << (ilit > 0));
				assert(av->assumed & bit);
				assert(!(av->failed & bit));
				av->failed |= bit;
			}
			size = lglcntstk(&lgl->clause);
			assert(size > 0);
			lglpushstk(lgl, &lgl->clause, 0);
			lglprt(lgl, 2, "[analyze-final] learned clause with size %d out of %d", size, lglcntstk(&lgl->eassume));
			LOGCLS(2, lgl->clause.start, "failed assumption clause");
			lgldrupligaddcls(lgl, REDCS);
			lgladdcls(lgl, REDCS, size, 0);
			lglpopstk(&lgl->clause);
			lglpopnunmarkstk(lgl, &lgl->clause);
		}
		count = 0;
		for (p = lgl->eassume.start; p < lgl->eassume.top; p++) {
			elit = *p;
			bit = 1u << (elit < 0);
			ext = lglelit2ext(lgl, elit);
			assert(!ext->eliminated && !ext->blocking);
			assert(ext->assumed & bit);
			if (ext->failed & bit)
				continue;
			if (ext->equiv) {
				erepr = ext->repr;
				rbit = bit;
				if (erepr < 0)
					rbit ^= 3;
				if (elit < 0)
					erepr = -erepr;
				rext = lglelit2ext(lgl, erepr);
				assert(!rext->equiv);
				if (rext->failed & rbit)
					continue;
				ilit = rext->repr;
				ibit = rbit;
				if (ilit < 0)
					ilit = -ilit, ibit ^= 3;
				if (ilit == 1)
					continue;
				assert(ilit && ilit != -1);
				av = lglavar(lgl, ilit);
				if (!(av->failed & ibit))
					continue;
				rext->failed |= rbit;
				count++;
				if (rext->assumed & rbit) {
					LOG(2, "found representative external failed assumption %d", erepr);
				} else {
					LOG(2, "found non representative external failed assumption %d", elit);
					ext->failed |= bit;
				}
			} else {
				ilit = ext->repr;
				ibit = bit;
				if (ilit < 0)
					ilit = -ilit, ibit ^= 3;
				if (ilit == 1)
					continue;
				assert(ilit && ilit != -1);
				av = lglavar(lgl, ilit);
				if (!(av->failed & ibit))
					continue;
				LOG(2, "found external failed assumption %d", elit);
				ext->failed |= bit;
				count++;
			}
		}
		LOG(1, "found %u external failed assumptions", count);
	}
	TRANS(FAILED);
}

static void
lglternreslit(LGL* lgl, int lit)
{
	int *pw, *peow, *nw, *neow, *p, *n;
	int pblit, ptag, pother, pother2, pdelta;
	int nblit, ntag, nother, nother2, ndelta;
	HTS *phts, *nhts;
	int a, b, c;

	phts = lglhts(lgl, lit);
	pw = lglhts2wchs(lgl, phts);
	peow = pw + phts->count;
	nhts = lglhts(lgl, -lit);
	nw = lglhts2wchs(lgl, nhts);
	neow = nw + nhts->count;
	for (n = nw; n < neow; n++) {
		if (INCSTEPS(ternres.steps) >= lgl->limits->ternres.steps)
			return;
		nblit = *n;
		ntag = nblit & MASKCS;
		if (ntag == BINCS || ntag == OCCS)
			continue;
		if (ntag == TRNCS)
			break;
		assert(ntag == LRGCS);
		n++;
	}
	if (n >= neow)
		return;
	for (p = pw; p < peow && lgl->stats->ternres.steps < lgl->limits->ternres.steps; p++) {
		INCSTEPS(ternres.steps);
		pblit = *p;
		ptag = pblit & MASKCS;
		if (ptag == BINCS || ptag == OCCS)
			continue;
		if (ptag == TRNCS || ptag == LRGCS)
			p++;
		if (ptag == LRGCS)
			continue;
		assert(ptag == TRNCS);
		pother = pblit >> RMSHFT;
		if (lglval(lgl, pother))
			continue;
		pother2 = *p;
		if (lglval(lgl, pother2))
			continue;
		for (n = nw; n < neow && lgl->stats->ternres.steps < lgl->limits->ternres.steps; n++) {
			INCSTEPS(ternres.steps);
			nblit = *n;
			ntag = nblit & MASKCS;
			if (ntag == BINCS || ntag == OCCS)
				continue;
			if (ntag == TRNCS || ntag == LRGCS)
				n++;
			if (ntag == LRGCS)
				continue;
			assert(ntag == TRNCS);
			nother = nblit >> RMSHFT;
			if (lglval(lgl, nother))
				continue;
			nother2 = *n;
			if (lglval(lgl, nother2))
				continue;
			if ((nother == pother && nother2 == pother2) || (nother == pother2 && nother2 == pother)) {
				a = nother, b = nother2;
				if (lglhasbin(lgl, a, b))
					continue;
				lgl->stats->ternres.bin++;
				LOG(2, "ternary resolvent %d %d", a, b);
				lgldrupligaddclsarg(lgl, REDCS, a, b, 0);
				lglwchbin(lgl, a, b, REDCS);
				lglwchbin(lgl, b, a, REDCS);
				lgl->stats->red.bin++, assert(lgl->stats->red.bin > 0);
				lglwrktouch(lgl, a);
				lglwrktouch(lgl, b);
			} else {
				a = nother, b = nother2;
				if (nother == pother || nother2 == pother)
					c = pother2;
				else if (nother == pother2 || nother2 == pother2)
					c = pother;
				else
					continue;
				assert(a != b && b != c && a != c);
				assert(a != -b);
				if (a == -c || b == -c)
					continue;
				if (lglhastrn(lgl, a, b, c))
					continue;
				lgl->stats->ternres.trn++;
				LOG(2, "ternary resolvent %d %d %d", a, b, c);
				lgldrupligaddclsarg(lgl, REDCS, a, b, c, 0);
				lglwchtrn(lgl, a, b, c, REDCS);
				lglwchtrn(lgl, b, a, c, REDCS);
				lglwchtrn(lgl, c, a, b, REDCS);
				lgl->stats->red.trn++, assert(lgl->stats->red.trn > 0);
				lglwrktouch(lgl, a);
				lglwrktouch(lgl, b);
				lglwrktouch(lgl, c);
			}
			pdelta = p - pw;
			phts = lglhts(lgl, lit);
			pw = lglhts2wchs(lgl, phts);
			peow = pw + phts->count;
			p = pw + pdelta;
			ndelta = n - nw;
			nhts = lglhts(lgl, -lit);
			nw = lglhts2wchs(lgl, nhts);
			neow = nw + nhts->count;
			n = nw + ndelta;
		}
	}
}

static void
lglternresidx(LGL* lgl, int idx)
{
	lglternreslit(lgl, idx);
	lglternreslit(lgl, -idx);
}

static void
lglseternreslim(LGL* lgl)
{
	int64_t limit, irrlim;
	int pen, szpen;
	if (lgl->opts->ternresrtc.val) {
		lgl->limits->ternres.steps = LLMAX;
		lglprt(lgl, 1, "[ternres-%d] really no limit (run to completion)", lgl->stats->ternres.count);
	} else {
		limit = (lgl->opts->trnreleff.val * lglvisearch(lgl)) / 1000;
		if (limit < lgl->opts->trnrmineff.val)
			limit = lgl->opts->trnrmineff.val;
		if (lgl->opts->trnrmaxeff.val >= 0 && limit > lgl->opts->trnrmaxeff.val)
			limit = lgl->opts->trnrmaxeff.val;
		if (lgl->stats->ternres.count <= 1 && lgl->opts->boost.val && lgl->opts->ternresboost.val > 1) {
			lglprt(lgl,
				   1,
				   "[ternres-%d] boosting ternary resolution limit by %d",
				   lgl->stats->ternres.count,
				   lgl->opts->ternresboost.val);
			limit *= lgl->opts->ternresboost.val;
		}
		limit >>= (pen = lgl->limits->ternres.pen + (szpen = lglszpen(lgl)));
		irrlim = (4 * lgl->stats->irr.clauses.cur) >> szpen;
		if (lgl->opts->irrlim.val && limit < irrlim) {
			limit = irrlim;
			lglprt(lgl,
				   1,
				   "[ternres-%d] limit %lld based on %d irredundant clauses penalty %d",
				   lgl->stats->ternres.count,
				   (LGLL)limit,
				   lgl->stats->irr.clauses.cur,
				   szpen);
		} else
			lglprt(lgl,
				   1,
				   "[ternres-%d] limit %lld with penalty %d = %d + %d",
				   lgl->stats->ternres.count,
				   (LGLL)limit,
				   pen,
				   lgl->limits->ternres.pen,
				   szpen);
		lgl->limits->ternres.steps = lgl->stats->ternres.steps + limit;
	}
}

static void
lglprternresrem(LGL* lgl)
{
	int idx, ret = 0, rem = 0, sum;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		if (lglavar(lgl, idx)->donoternres)
			ret++;
		else
			rem++;
	}
	if (rem) {
		sum = ret + rem;
		lglprt(lgl,
			   1,
			   "[ternres-%d] %d variables remain %.0f%% (%d retained %.0f%%)",
			   lgl->stats->ternres.count,
			   rem,
			   lglpcnt(rem, sum),
			   ret,
			   lglpcnt(ret, sum));
	} else {
		lglprt(lgl, 1, "[ternres-%d] fully completed ternary resolution", lgl->stats->ternres.count);
		for (idx = 2; idx < lgl->nvars; idx++)
			lglavar(lgl, idx)->donoternres = 0;
	}
}

static void
lglternresinit(LGL* lgl)
{
	int idx, schedulable = 0, donoternres = 0;
	lglwrkinit(lgl, 1, 1);
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		if (lglavar(lgl, idx)->donoternres)
			donoternres++;
		else
			schedulable++;
	}
	if (!schedulable) {
		donoternres = 0;
		for (idx = 2; idx < lgl->nvars; idx++) {
			if (!lglisfree(lgl, idx))
				continue;
			lglavar(lgl, idx)->donoternres = 0;
			schedulable++;
		}
	}
	if (!donoternres)
		lglprt(lgl, 1, "[ternres-%d] all %d free variables schedulable", lgl->stats->ternres.count, schedulable);
	else
		lglprt(lgl,
			   1,
			   "[ternres-%d] %d schedulable variables %.0f%%",
			   lgl->stats->ternres.count,
			   schedulable,
			   lglpcnt(schedulable, lgl->nvars - 2));
	assert(!lgl->donotsched), lgl->donotsched = 1;
	lglrandidxtrav(lgl, lglwrktouch);
	assert(lgl->donotsched), lgl->donotsched = 0;
}

static int
lglternres(LGL* lgl)
{
	int before, after, delta;
	int before2, after2, delta2;
	int before3, after3, delta3;
	int success, lit;
	if (lgl->nvars <= 2)
		return 1;
	lglstart(lgl, &lgl->times->ternres);
	ASSERT(!lgl->simp && !lgl->ternresing);
	lgl->simp = lgl->ternresing = 1;
	lgl->stats->ternres.count++;
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	lglseternreslim(lgl);

	lglternresinit(lgl);

	before2 = lgl->stats->ternres.bin;
	before3 = lgl->stats->ternres.trn;
	while (lgl->stats->ternres.steps < lgl->limits->ternres.steps) {
		if (lglterminate(lgl))
			break;
		if (!lglsyncunits(lgl))
			break;
		if (!(lit = lglwrknext(lgl))) {
			lglprt(lgl, 2, "[ternres-%d] saturated", lgl->stats->ternres.count);
			break;
		}
		INCSTEPS(ternres.steps);
		assert(lit > 0);
		if (!lglisfree(lgl, lit))
			continue;
		lglavar(lgl, lit)->donoternres = 1;
		lglternresidx(lgl, lit);
	}
	after2 = lgl->stats->ternres.bin;
	after3 = lgl->stats->ternres.trn;
	after = after2 + after3;
	before = before2 + before3;
	delta2 = after2 - before2;
	delta3 = after3 - before3;
	delta = after - before;
	success = before < after;
	lglprt(lgl,
		   1,
		   "[ternres-%d] %d ternary resolvents (%d bin, %d trn)",
		   lgl->stats->ternres.count,
		   delta,
		   delta2,
		   delta3);
	LGLUPDPEN(ternres, success);
	assert(lgl->simp && lgl->ternresing);
	lgl->simp = lgl->ternresing = 0;
	lglprternresrem(lgl);
	lglrep(lgl, 2, '3');
	lglwrkreset(lgl);
	lglstop(lgl);
	return !lgl->mt;
}

typedef struct POSLIDX
{
	int pos, lidx;
} POSLIDX;

int
lglcmposlidx(LGL* lgl, int* lits, POSLIDX* a, POSLIDX* b)
{
	const int *c = lits + a->pos, *d = lits + b->pos;
	int i, l, k, p, q;
	for (i = 0; i < 4; i++) {
		l = c[i], k = d[i];
		ASSERT(l != INT_MIN);
		ASSERT(k != INT_MIN);
		p = abs(l), q = abs(k);
		if (p < q)
			return -1;
		if (p > q)
			return 1;
		if (l < k)
			return -1;
		if (l > k)
			return 1;
	}
	(void)lgl;
	return 0;
}

#define LGLCMPOSLIDX(A, B) lglcmposlidx(lgl, lits.start, (A), (B))

static int
lglquatres1(LGL* lgl, int* trnptr)
{
	int mask, size, lidx, count, lrg, total, tlrg, pos, m[4], i, j, k, l, n;
	int glue, maxglue = lglscaleglue(lgl, 4), pivot, a, b, redi, redj, red;
	int assigned, trn, sub;
	const int *p, *c, *d;
	POSLIDX* pls;
	Stk lits, clauses, *s;
	assert(!lgl->level);
	assert(lgl->notrim);
	lglstart(lgl, &lgl->times->quatres1);
	CLR(lits);
	CLR(clauses);
	total = trn = sub = tlrg = 0;
	for (glue = -1; glue <= maxglue; glue++) {
		count = lrg = 0;
		if (glue < 0)
			mask = MAXGLUE, s = &lgl->irr;
		else
			mask = glue, s = lgl->red + glue;
		for (c = s->start; c < s->top; c = p + 1) {
			if (*c >= REMOVED) {
				p = c;
				continue;
			}
			if (glue >= 0)
				assert(lglisact(*c)), c++;
			assigned = 0;
			for (p = c; *p; p++)
				if (lglval(lgl, *p))
					assigned++;
			if (assigned)
				continue;
			size = p - c;
			lrg++;
			if (size > 4)
				continue;
			assert(size == 4);
			lidx = c - s->start;
			assert(lidx >= 0);
			lidx <<= GLUESHFT;
			lidx |= mask;
			pos = lglcntstk(&lits);
			assert(!(pos & 3));
			lglpushstk(lgl, &clauses, pos);
			lglpushstk(lgl, &clauses, lidx);
			for (i = 0; i < 4; i++)
				m[i] = c[i];
			for (i = 0; i < 3; i++)
				for (j = i + 1; j < 4; j++)
					if (abs(m[i]) > abs(m[j]))
						SWAP(int, m[i], m[j]);
			for (i = 0; i < 4; i++)
				lglpushstk(lgl, &lits, m[i]);
			count++;
		}
		if (glue < 0)
			lglprt(lgl,
				   2,
				   "[quatres-%d] found %d irredundant quaternary clauses %.0f%%",
				   lgl->stats->quatres.count,
				   count,
				   lglpcnt(count, lrg));
		else
			lglprt(lgl,
				   2,
				   "[quatres-%d] found %d glue %d quaternary clauses %.0f%%",
				   lgl->stats->quatres.count,
				   count,
				   glue,
				   lglpcnt(count, lrg));
		total += count;
		tlrg += lrg;
	}
	lglfitstk(lgl, &lits), lglfitstk(lgl, &clauses);
	lglprt(lgl,
		   1 + !tlrg,
		   "[quatres-%d] found %d quaternary clauses %.0f%% in total",
		   lgl->stats->quatres.count,
		   total,
		   lglpcnt(total, tlrg));
	pls = (POSLIDX*)clauses.start;
	assert((int)lglcntstk(&clauses) == 2 * total);
	SORT(POSLIDX, pls, total, LGLCMPOSLIDX);
#if 0
  for (i = 0; i < total; i++) {
    c = lits.start + pls[i].pos;
    lidx = pls[i].lidx;
    printf ("c pls[%d] %d %d %d %d (glue %d, pos %d)\n",
      i,
      c[0], c[1], c[2], c[3],
      (lidx & GLUEMASK), (lidx >> GLUESHFT));
  }
#endif
	for (i = 0; i < total - 1; i++) {
		pos = pls[i].pos;
		if (pos < 0)
			continue;
		c = lits.start + pos;
		for (j = i + 1; j < total; j++) {
			if (pls[i].pos < 0)
				break;
			pos = pls[j].pos;
			if (pos < 0)
				break;
			d = lits.start + pos;
			pivot = 0;
			for (k = 0; k < 4; k++) {
				a = c[k], b = d[k];
				if (abs(a) != abs(b))
					break;
				if (a == b)
					continue;
				if (pivot)
					break;
				pivot = a;
			}
			if (k < 4)
				break;
			assert(i != j);
			assert(pls[i].lidx != pls[j].lidx);
			redi = ((pls[i].lidx & GLUEMASK) == MAXGLUE) ? 0 : REDCS;
			redj = ((pls[j].lidx & GLUEMASK) == MAXGLUE) ? 0 : REDCS;
			if (pivot) {
				n = 0;
				for (l = 0; l < 4; l++)
					if (c[l] != pivot)
						m[n++] = c[l];
				assert(n == 3);
				if (lglhastrn(lgl, m[0], m[1], m[2]))
					continue;
				red = redi & redj;
				LOG(2,
					"self-subsuming %s ternary resolvent %d %d %d",
					red ? "redundant" : "irredundant",
					m[0],
					m[1],
					m[2]);
				lgldrupligaddclsarg(lgl, REDCS, m[0], m[1], m[2], 0);
				lglwchtrn(lgl, m[0], m[1], m[2], red);
				lglwchtrn(lgl, m[1], m[0], m[2], red);
				lglwchtrn(lgl, m[2], m[0], m[1], red);
				if (!red)
					lglincirr(lgl, 3);
				else
					lgl->stats->red.trn++, assert(lgl->stats->red.trn > 0);
				lgl->stats->quatres.self2++;
				*trnptr += 1, trn++;
				lgldrupligdelclsarg(lgl, c[0], c[1], c[2], c[3], 0);
				if (redi)
					lglrmlcls(lgl, pls[i].lidx, REDCS);
				else
					lglrmlcls(lgl, (pls[i].lidx >> GLUESHFT), 0);
				lgldrupligdelclsarg(lgl, d[0], d[1], d[2], d[3], 0);
				if (redj)
					lglrmlcls(lgl, pls[j].lidx, REDCS);
				else
					lglrmlcls(lgl, (pls[j].lidx >> GLUESHFT), 0);
				pls[i].pos = pls[j].pos = -1;
			} else if (redi) {
				lgl->stats->quatres.dup++, sub++;
				lgldrupligdelclsarg(lgl, c[0], c[1], c[2], c[3], 0);
				lglrmlcls(lgl, pls[i].lidx, REDCS);
				pls[i].pos = -1;
			} else if (redj) {
				lgl->stats->quatres.dup++;
				lgldrupligdelclsarg(lgl, d[0], d[1], d[2], d[3], 0);
				lglrmlcls(lgl, pls[j].lidx, REDCS);
				pls[j].pos = -1;
			} else {
				lgl->stats->quatres.dup++;
				lgldrupligdelclsarg(lgl, d[0], d[1], d[2], d[3], 0);
				lglrmlcls(lgl, (pls[j].lidx >> GLUESHFT), 0);
				pls[j].pos = -1;
			}
		}
	}
	lglrelstk(lgl, &lits), lglrelstk(lgl, &clauses);
	lglprt(lgl, 1 + !sub, "[quatres-%d-1] removed %d duplicate quaternary clauses", lgl->stats->quatres.count, sub);
	lglprt(lgl,
		   1 + !trn,
		   "[quatres-%d-1] added %d double-self-subsuming ternary resolvents",
		   lgl->stats->quatres.count,
		   trn);
	lglstop(lgl);
	return tlrg;
}

static int
lglhasquad(LGL* lgl)
{
	int blit, tag, red, other, other2, lidx, glue, count, lit, other3, val;
	const int *p, *w, *eow, *q, *c, *d, *r, *l;
	int maxglue = lglscaleglue(lgl, 4);
	int maxcount, tmpcount, maxlit;
	HTS* hts;
	assert(lglcntstk(&lgl->clause) == 5);
	maxcount = -1, maxlit = 0;
	for (p = (c = lgl->clause.start); (lit = *p); p++) {
		assert(!lglval(lgl, lit));
		tmpcount = lglhts(lgl, lit)->count;
		if (tmpcount <= maxcount)
			continue;
		maxcount = tmpcount, maxlit = lit;
	}
	assert(maxlit), assert(maxcount >= 0);
	for (p = c; (lit = *p); p++) {
		if (lit == maxlit)
			continue;
		hts = lglhts(lgl, lit);
		w = lglhts2wchs(lgl, hts);
		eow = w + hts->count;
		for (q = w; q < eow; q++) {
			blit = *q;
			tag = blit & MASKCS;
			if (tag == BINCS) {
				other = blit >> RMSHFT;
				for (r = c; (other3 = *r); r++)
					if (other3 == other)
						return 1;
			} else if (tag == TRNCS) {
				other = blit >> RMSHFT;
				other2 = *++q;
				count = 0;
				for (r = c; (other3 = *r); r++) {
					if (other3 == other) {
						if (++count == 2)
							return 1;
					} else if (other3 == other2) {
						if (++count == 2)
							return 1;
					}
				}
			} else {
				assert(tag == LRGCS);
				red = blit & REDCS;
				lidx = *++q;
				if (red) {
					glue = lidx & GLUEMASK;
					if (glue > maxglue)
						continue;
				}
				d = lglidx2lits(lgl, red, lidx);
				count = 0;
				for (l = d; (other = *l); l++) {
					val = lglval(lgl, other);
					if (val > 0)
						break;
					if (val < 0)
						continue;
					if (count >= 4)
						break;
					for (r = c; (other3 = *r); r++)
						if (other3 == other)
							break;
					if (!other3)
						break;
					count++;
				}
				if (!other)
					return 1;
			}
		}
	}
	return 0;
}

static void
lglquatres2(LGL* lgl, int* trnptr, int* quadptr)
{
	int lit, sign, other, blit, tag, red, lidx, trn, quat, glue, glue2;
	int count, pos, neg, extra, pivot, other2, other3, rednew;
	const int *start, *c, *p, *w, *eow, *q, *d, *l;
	int maxcheck = lgl->stats->quatres.count, check;
	int maxglue = lglscaleglue(lgl, 4), val;
	ptrdiff_t delta;
	HTS* hts;
	Stk* s;
	lglstart(lgl, &lgl->times->quatres2);
	assert(!lgl->level);
	assert(lgl->notrim);
	trn = quat = 0;
	for (glue = -1; glue <= maxglue; glue++) {
		s = (glue < 0) ? &lgl->irr : lgl->red + glue;
		for (c = (start = s->start); c < s->top; c = p + 1) {
			if (*c >= REMOVED) {
				p = c;
				continue;
			}
			if (glue >= 0)
				assert(lglisact(*c)), c++;
			count = 0;
			for (p = c; (lit = *p); p++) {
				val = lglval(lgl, lit);
				if (val < 0)
					continue;
				if (val > 0)
					break;
				if (++count > 4)
					break;
			}
			if (lit) {
				while (*++p)
					;
				continue;
			}
			assert(count <= 4);
			if (count < 4)
				continue;
			for (p = c; (lit = *p); p++)
				if (!lglval(lgl, lit))
					lglmark(lgl, lit);
			for (p = c; (lit = *p); p++) {
				if (lglval(lgl, lit))
					continue;
				for (sign = -1; sign <= 1; sign += 2) {
					hts = lglhts(lgl, sign * lit);
					w = lglhts2wchs(lgl, hts);
					eow = w + hts->count;
					check = 0;
					for (q = w; check < maxcheck && q < eow; q++) {
						blit = *q;
						tag = blit & MASKCS;
						red = blit & REDCS;
						if (tag == TRNCS || tag == LRGCS)
							q++;
						if (maxglue < MAXGLUE && tag == LRGCS) {
							lidx = *q;
							if (red) {
								glue2 = lidx & GLUEMASK;
								if (glue2 > maxglue)
									continue;
							} else
								glue2 = -1;
							if (glue2 < glue)
								continue;
							d = lglidx2lits(lgl, red, lidx);
							if (d == c)
								continue;
							if (glue2 == glue && d < c)
								continue;
							check++;
							count = pos = neg = extra = pivot = 0;
							for (l = d; (other = *l); l++) {
								val = lglval(lgl, other);
								if (val < 0)
									continue;
								if (val > 0)
									break;
								val = lglmarked(lgl, other);
								if (val < 0) {
									if (neg++)
										break;
									pivot = other;
								} else if (val > 0) {
									if (++pos > 2)
										break;
								} else {
									assert(!val);
									if (count++)
										break;
									extra = other;
								}
							}
							if (other)
								continue;
							if (neg != 1 || pos != 2 || count != 1)
								continue;
							assert(extra);
							assert(lglmtstk(&lgl->clause));
							for (l = c; (other = *l); l++) {
								if (lglval(lgl, other))
									continue;
								if (other == pivot)
									continue;
								if (other == -pivot)
									continue;
								lglpushstk(lgl, &lgl->clause, other);
							}
							lglpushstk(lgl, &lgl->clause, extra);
							lglpushstk(lgl, &lgl->clause, 0);
							if (!lglhasquad(lgl)) {
								LOGCLS(0, c, "1st pivot %d quaternary scaled glue %d antecedent", -pivot, glue);
								LOGCLS(0, d, "2nd pivot %d quaternary scaled glue %d antecedent", pivot, glue2);
								LOGCLS(0, lgl->clause.start, "found quaternary resolvent");
								LOG(5, "before: w + %d = q = eow - %d", (int)(q - w), (int)(eow - w));
								lgldrupligaddcls(lgl, REDCS);
								lgladdcls(lgl, REDCS, 4, 0);
								delta = s->start - start;
								if (delta) {
									LOG(5, "fixing glue %d clauses start by %ld", glue, delta);
									c += delta, p += delta, start = s->start;
								}
								hts = lglhts(lgl, sign * lit);
								delta = lglhts2wchs(lgl, hts) - w;
								if (delta) {
									LOG(5, "fixing %d watches start by %ld", sign * lit, delta);
									w += delta, q += delta;
								}
								if (w + hts->count != eow) {
									LOG(5, "fixing %d watches end from %x to %x", sign * lit, eow, w + hts->count);
									eow = w + hts->count;
								}
								LOG(5, "after: w + %d = q = eow - %d", (int)(q - w), (int)(eow - w));
								lgl->stats->quatres.quat++;
								*quadptr += 1, quat++;
							}
							lglclnstk(&lgl->clause);
						} else if (tag == TRNCS) {
							if (sign > 0)
								continue;
							other = blit >> RMSHFT;
							if (lglval(lgl, other))
								continue;
							if (lglmarked(lgl, other) <= 0)
								continue;
							other2 = *q;
							if (lglval(lgl, other2))
								continue;
							if (lglmarked(lgl, other2) <= 0)
								continue;
							// This gives a usefull ternary self-subsuming resolvent:
							// (lit A B C) resolved with (-lit A B) gives (A B C)
							// Other cases produce too many quaternary resolvents (b7ztz6).
							LOGCLS(0, c, "1st pivot %d quaternary scaled glue %d antecedent", lit, glue);
							LOG(0, "2nd pivot %d ternary antecedent %d %d %d", -lit, -lit, other, other2);
							extra = 0;
							for (l = c; !extra; l++) {
								other3 = *l;
								assert(other3);
								if (other3 == lit)
									continue;
								assert(other3 != -lit);
								if (other3 == other)
									continue;
								if (other3 == other2)
									continue;
								val = lglval(lgl, other3);
								assert(val <= 0);
								if (val)
									continue;
								extra = other3;
							}
							assert(extra != -other);
							assert(extra != -other2);
							rednew = (glue >= 0) ? REDCS : 0;
							LOG(0,
								"self-subsuming %s ternary resolvent %d %d %d",
								rednew ? "redundant" : "irredundant",
								other,
								other2,
								extra);
							lgldrupligaddclsarg(lgl, REDCS, other, other2, extra, 0);
							lglwchtrn(lgl, other, other2, extra, rednew);
							lglwchtrn(lgl, other2, other, extra, rednew);
							lglwchtrn(lgl, extra, other, other2, rednew);
							// NOTE: 'hts', 'q', 'w', 'eow' might be invalid now.
							// But 'c' has to remain valid:
							assert(s->start == start), assert(c + 4 < s->top);
							if (!rednew)
								lglincirr(lgl, 3);
							else
								lgl->stats->red.trn++, assert(lgl->stats->red.trn > 0);
							lgl->stats->quatres.self1++;
							*trnptr += 1, trn++;
							for (p = c; (other3 = *p); p++) // set 'p' to point after 'c'
								if (!lglval(lgl, other3))	// reset marks now because
									lglunmark(lgl, other3); // 'lglrmlcls' invalidates 'c'
							lidx = c - start;
							if (rednew) {
								assert(lidx >= 0);
								lidx <<= GLUESHFT;
								assert(lidx >= 0);
								assert(0 <= glue), assert(glue <= MAXGLUE);
								lidx |= glue;
							}
							lgldrupligdelclsaux(lgl, c);
							assert(!*p);
							lglrmlcls(lgl, lidx, rednew); // rednew = redold (of 'c')
							assert(start <= c), assert(c + 3 < p), assert(p <= s->top);
							assert(p[-0] == REMOVED);
							assert(p[-1] == REMOVED);
							assert(p[-2] == REMOVED);
							assert(p[-3] == REMOVED);
							assert(p[-4] == REMOVED), assert(c[0] == REMOVED);
							goto NEXT_CLAUSE;
						}
					}
				}
			}
			for (p = c; (lit = *p); p++)
				if (!lglval(lgl, lit))
					lglunmark(lgl, lit);
		NEXT_CLAUSE:;
		}
	}
	lglprt(lgl,
		   1 + !trn,
		   "[quatres-%d-2] added %d single-self-subsuming ternary resolvents",
		   lgl->stats->quatres.count,
		   trn);
	lglprt(lgl, 1 + !quat, "[quatres-%d-2] added %d quaternary resolvents", lgl->stats->quatres.count, quat);
	lglstop(lgl);
}

static void
lglquatres(LGL* lgl)
{
	int trn, quad;
	lglstart(lgl, &lgl->times->quatres);
	if (lgl->level)
		lglbacktrack(lgl, 0);
	assert(!lgl->simp), assert(!lgl->quatres), assert(!lgl->notrim);
	lgl->simp = lgl->quatres = lgl->notrim = 1;
	lgl->stats->quatres.count++;
	trn = quad = 0;
	if (lglquatres1(lgl, &trn))
		lglquatres2(lgl, &trn, &quad);
	LGLUPDPEN(quatres, trn + quad);
	assert(lgl->simp), assert(lgl->quatres), assert(lgl->notrim);
	lgl->simp = lgl->quatres = lgl->notrim = 0;
	lglrep(lgl, 2, '4');
	lglstop(lgl);
}

static int
lgltrdbin(LGL* lgl, int start, int target, int irr)
{
	int lit, next, blit, tag, red, other, *p, *w, *eow, res, ign, val;
	HTS* hts;
	assert(lglmtstk(&lgl->seen));
	assert(abs(start) < abs(target));
	LOG(2, "trying transitive reduction of %s binary clause %d %d", lglred2str(irr ^ REDCS), start, target);
	lgl->stats->trd.bins++;
	lglpushnmarkseen(lgl, -start);
	next = 0;
	res = 0;
	ign = 1;
	while (next < (int)lglcntstk(&lgl->seen)) {
		lit = lglpeek(&lgl->seen, next++);
		INCSTEPS(trd.steps);
		LOG(3, "transitive reduction search step %d", lit);
		val = lglval(lgl, lit);
		if (val)
			continue;
		hts = lglhts(lgl, -lit);
		if (!hts->count)
			continue;
		w = lglhts2wchs(lgl, hts);
		eow = w + hts->count;
		for (p = w; p < eow; p++) {
			blit = *p;
			tag = blit & MASKCS;
			if (tag == LRGCS || tag == TRNCS)
				p++;
			if (tag != BINCS)
				continue;
			red = blit & REDCS;
			if (irr && red)
				continue;
			other = blit >> RMSHFT;
			if (other == start)
				continue;
			if (other == target) {
				if (lit == -start && ign) {
					ign = 0;
					continue;
				}
				LOG(2, "transitive path closed with %s binary clause %d %d", lglred2str(red), -lit, other);
				res = 1;
				goto DONE;
			}
			val = lglmarked(lgl, other);
			if (val > 0)
				continue;
			if (val < 0) {
				assert(lgl->level == 0);
				lgl->stats->trd.failed++;
				LOG(1, "failed literal %d in transitive reduction", -start);
				lglunit(lgl, start);
				val = lglbcp(lgl);
				if (!val && !lgl->mt)
					lglmt(lgl);
				assert(val || lgl->mt);
				res = -1;
				goto DONE;
			}
			lglpushnmarkseen(lgl, other);
			LOG(3, "transitive reduction follows %s binary clause %d %d", lglred2str(red), -lit, other);
		}
	}
DONE:
	lglpopnunmarkstk(lgl, &lgl->seen);
	return res;
}

static void
lgltrdlit(LGL* lgl, int start)
{
	int target, *w, *p, *eow, blit, tag, red, val;
#ifndef NDEBUG
	int unassigned = lgl->unassigned;
#endif
	HTS* hts;
	val = lglval(lgl, start);
	if (val)
		return;
	LOG(2, "transitive reduction of binary clauses with %d", start);
	assert(lglmtstk(&lgl->seen));
	hts = lglhts(lgl, start);
	if (!hts->count)
		return;
	lgl->stats->trd.lits++;
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow && (lgl->stats->trd.steps < lgl->limits->trd.steps); p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag != BINCS)
			continue;
		target = blit >> RMSHFT;
		if (abs(start) > abs(target))
			continue;
		red = blit & REDCS;
		val = lgltrdbin(lgl, start, target, red ^ REDCS);
		if (!val)
			continue;
		if (val < 0) {
			assert(lgl->mt || lgl->unassigned < unassigned);
			break;
		}
		LOG(2, "removing transitive redundant %s binary clause %d %d", lglred2str(red), start, target);
		lgl->stats->trd.red++;
		lgl->stats->prgss++;
		lgldrupligdelclsarg(lgl, start, target, 0);
		lglrmbwch(lgl, start, target, red);
		lglrmbwch(lgl, target, start, red);
		assert(!lgl->dense);
		if (red)
			assert(lgl->stats->red.bin > 0), lgl->stats->red.bin--;
		else
			lgldecirr(lgl, 2);
		break;
	}
}

static void
lglsetrdlim(LGL* lgl)
{
	int64_t limit, irrlim;
	int pen, szpen;
	limit = (lgl->opts->trdreleff.val * lglvisearch(lgl)) / 1000;
	if (limit < lgl->opts->trdmineff.val)
		limit = lgl->opts->trdmineff.val;
	if (lgl->opts->trdmaxeff.val >= 0 && limit > lgl->opts->trdmaxeff.val)
		limit = lgl->opts->trdmaxeff.val;
	limit >>= (pen = lgl->limits->trd.pen + (szpen = lglszpen(lgl)));
	irrlim = (lgl->stats->irr.clauses.cur) >> szpen;
	if (lgl->opts->irrlim.val && limit < irrlim) {
		limit = irrlim;
		lglprt(lgl,
			   1,
			   "[transred-%d] limit %lld based on %d irredundant clauses penalty %d",
			   lgl->stats->trd.count,
			   (LGLL)limit,
			   lgl->stats->irr.clauses.cur,
			   szpen);
	} else
		lglprt(lgl,
			   1,
			   "[transred-%d] limit %lld with penalty %d = %d + %d",
			   lgl->stats->trd.count,
			   (LGLL)limit,
			   pen,
			   lgl->limits->trd.pen,
			   szpen);
	lgl->limits->trd.steps = lgl->stats->trd.steps + limit;
}

static int
lgltrd(LGL* lgl)
{
	unsigned pos, delta, mod, ulit, first, last;
	int failed = lgl->stats->trd.failed;
	int red = lgl->stats->trd.red;
	int lit, count, success;
	if (lgl->nvars <= 2)
		return 1;
	lgl->stats->trd.count++;
	lglstart(lgl, &lgl->times->transred);
	assert(!lgl->simp);
	lgl->simp = 1;
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	lglsetrdlim(lgl);
	mod = 2 * (lgl->nvars - 2);
	assert(mod > 0);
	pos = lglrand(lgl) % mod;
	delta = lglrand(lgl) % mod;
	if (!delta)
		delta++;
	while (lglgcd(delta, mod) > 1)
		if (++delta == mod)
			delta = 1;
	LOG(1, "transitive reduction start %u delta %u mod %u", pos, delta, mod);
	first = mod;
	count = 0;
	while (lgl->stats->trd.steps < lgl->limits->trd.steps) {
		if (lglterminate(lgl))
			break;
		if (!lglsyncunits(lgl))
			break;
		ulit = pos + 4;
		lit = lglilit(ulit);
		lgltrdlit(lgl, lit);
		count++;
		assert((unsigned)count <= mod);
		if (lgl->mt)
			break;
		last = pos;
		pos += delta;
		if (pos >= mod)
			pos -= mod;
		if (pos == first) {
			assert((unsigned)count == mod);
			break;
		}
		if (mod == 1)
			break;
		if (first == mod)
			first = last;
	}
	failed = lgl->stats->trd.failed - failed;
	red = lgl->stats->trd.red - red;
	success = failed || red;
	LGLUPDPEN(trd, success);
	assert(lgl->simp);
	lglprt(lgl, 1, "[transred-%d] removed %d transitive binary clauses", lgl->stats->trd.count, red);
	lglprt(lgl, 1 + !failed, "[transred-%d] found %d units", lgl->stats->trd.count, failed);
	lgl->simp = 0;
	lglrep(lgl, 2, 'D');
	lglstop(lgl);
	return !lgl->mt;
}

static int
lglunhdhasbins(LGL* lgl, const DFPR* dfpr, int lit, int irronly)
{
	int blit, tag, other, val, red, ulit;
	const int *p, *w, *eos;
	HTS* hts;
	assert(!lglval(lgl, lit));
	hts = lglhts(lgl, lit);
	w = lglhts2wchs(lgl, hts);
	eos = w + hts->count;
	for (p = w; p < eos; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == OCCS)
			continue;
		if (tag == TRNCS || tag == LRGCS) {
			p++;
			continue;
		}
		red = blit & REDCS;
		if (irronly && red)
			continue;
		other = blit >> RMSHFT;
		val = lglval(lgl, other);
		assert(val >= 0);
		if (val > 0)
			continue;
		ulit = lglulit(other);
		if (!dfpr[ulit].discovered)
			return 1;
	}
	return 0;
}

static int
lglunhdisroot(LGL* lgl, int lit, DFPR* dfpr, int irronly)
{
	int res = !lglunhdhasbins(lgl, dfpr, lit, irronly);
	assert(!res || !dfpr[lglulit(lit)].discovered);
	return res;
}

static int
lglmtwtk(Wtk* wtk)
{
	return wtk->top == wtk->start;
}

static int
lglfullwtk(Wtk* wtk)
{
	return wtk->top == wtk->end;
}

static int
lglsizewtk(Wtk* wtk)
{
	return wtk->end - wtk->start;
}

static int
lglcntwtk(Wtk* wtk)
{
	return wtk->top - wtk->start;
}

static void
lglrelwtk(LGL* lgl, Wtk* wtk)
{
	DEL(wtk->start, lglsizewtk(wtk));
	memset(wtk, 0, sizeof *wtk);
}

static void
lglenlwtk(LGL* lgl, Wtk* wtk)
{
	int oldsize = lglsizewtk(wtk);
	int newsize = oldsize ? 2 * oldsize : 1;
	int count = lglcntwtk(wtk);
	RSZ(wtk->start, oldsize, newsize);
	wtk->top = wtk->start + count;
	wtk->end = wtk->start + newsize;
}

static void
lglpushwtk(LGL* lgl, Wtk* wtk, Wrag wrag, int lit, int other, int red)
{
	Work w;
	if (lglfullwtk(wtk))
		lglenlwtk(lgl, wtk);
	w.wrag = wrag;
	w.other = other;
	w.red = red ? 1 : 0;
	w.removed = 0;
	w.lit = lit;
	*wtk->top++ = w;
}

static int
lglstamp(LGL* lgl,
		 int root,
		 DFPR* dfpr,
		 DFOPF* dfopf,
		 Wtk* work,
		 Stk* units,
		 Stk* sccs,
		 Stk* trds,
		 int* visitedptr,
		 int stamp,
		 int irronly)
{
	int uroot, lit, ulit, blit, tag, red, other, failed, uother, unotother;
	int observed, discovered, pos, undiscovered;
	unsigned start, end, mod, i, j, sccsize;
	const int *p, *w, *eos;
	int startstamp;
	const Work* r;
	int removed;
	HTS* hts;
	Wrag wrag;
	if (lglval(lgl, root))
		return stamp;
	uroot = lglulit(root);
	if (dfpr[uroot].discovered)
		return stamp;
	assert(!dfpr[uroot].finished);
	assert(lglmtwtk(work));
	assert(lglmtstk(units));
	assert(lglmtstk(sccs));
	assert(lglmtstk(trds));
	LOG(3,
		"stamping dfs %s %d %s",
		(lglunhdisroot(lgl, root, dfpr, irronly) ? "root" : "start"),
		root,
		irronly ? "only over irredundant clauses" : "also over redundant clauses");
	startstamp = 0;
	lglpushwtk(lgl, work, PREFIX, root, 0, 0);
	while (!lglmtwtk(work)) {
		INCSTEPS(unhd.steps);
		LGLPOPWTK(work, wrag, lit, other, red, removed);
		if (removed)
			continue;
		if (wrag == PREFIX) {
			ulit = lglulit(lit);
			if (dfpr[ulit].discovered) {
				dfopf[ulit].observed = stamp;
				LOG(3, "stamping %d observed %d", lit, stamp);
				continue;
			}
			assert(!dfpr[ulit].finished);
			dfpr[ulit].discovered = ++stamp;
			dfopf[ulit].observed = stamp;
			LOG(3, "stamping %d observed %d", lit, stamp);
			*visitedptr += 1;
			if (!startstamp) {
				startstamp = stamp;
				LOG(3, "root %d with stamp %d", lit, startstamp);
				dfpr[ulit].root = lit;
				LOG(4, "stamping %d root %d", lit, lit);
				assert(!dfpr[ulit].parent);
				LOG(4, "stamping %d parent %d", lit, 0);
			}
			LOG(4, "stamping %d discovered %d", lit, stamp);
			lglpushwtk(lgl, work, POSTFIX, lit, 0, 0);
			assert(dfopf[ulit].pushed < 0);
			dfopf[ulit].pushed = lglcntwtk(work);
			assert(!dfopf[ulit].flag);
			dfopf[ulit].flag = 1;
			lglpushstk(lgl, sccs, lit);
			hts = lglhts(lgl, -lit);
			w = lglhts2wchs(lgl, hts);
			eos = w + hts->count;
			for (undiscovered = 0; undiscovered <= 1; undiscovered++) {
				start = lglcntwtk(work);
				for (p = w; p < eos; p++) {
					blit = *p;
					tag = blit & MASKCS;
					if (tag == OCCS)
						continue;
					if (tag == TRNCS || tag == LRGCS) {
						p++;
						continue;
					}
					assert(tag == BINCS);
					red = blit & REDCS;
					if (irronly && red)
						continue;
					other = blit >> RMSHFT;
					if (lglval(lgl, other))
						continue;
					uother = lglulit(other);
					if (undiscovered != !dfpr[uother].discovered)
						continue;
					// Kind of defensive, since 'lglrmbindup' should avoid it
					// and this fix may not really work anyhow since it does
					// not distinguish between irredundant and redundant clauses.
					// Thus we put a hard COVER here.
					COVER(lglsignedmarked(lgl, other) > 0);
					if (lglsignedmarked(lgl, other) > 0) {
						LOG(2, "stamping skips duplicated edge %d %d", lit, other);
						continue;
					}
					lglsignedmark(lgl, other);
					lglpushwtk(lgl, work, BEFORE, lit, other, red);
				}
				end = lglcntwtk(work);
				for (r = work->start + start; r < work->top; r++)
					lglunmark(lgl, r->other);
				mod = (end - start);
				if (mod <= 1)
					continue;
				for (i = start; i < end - 1; i++) {
					assert(1 < mod && mod == (end - i));
					j = lglrand(lgl) % mod--;
					if (!j)
						continue;
					j = i + j;
					SWAP(Work, work->start[i], work->start[j]);
				}
			}
		} else if (wrag == BEFORE) { // before recursive call
			LOG(2, "stamping edge %d %d before recursion", lit, other);
			lglpushwtk(lgl, work, AFTER, lit, other, red);
			ulit = lglulit(lit);
			uother = lglulit(other);
			unotother = lglulit(-other);
			if (lgl->opts->unhdextstamp.val && (irronly || red) && dfopf[uother].observed > dfpr[ulit].discovered) {
				LOG(2, "transitive edge %d %d during stamping", lit, other);
				lgl->stats->unhd.stamp.trds++;
				lgl->stats->prgss++;
				if (red)
					lgl->stats->unhd.tauts.red++;
				lglrmbcls(lgl, -lit, other, red);
				if ((pos = dfopf[unotother].pushed) >= 0) {
					while (pos < lglcntwtk(work)) {
						if (work->start[pos].lit != -other)
							break;
						if (work->start[pos].other == -lit) {
							LOG(3, "removing edge %d %d from DFS stack", -other, -lit);
							work->start[pos].removed = 1;
						}
						pos++;
					}
				}
				work->top--;
				assert(dfpr[uother].discovered); // and thus 'parent' + 'root' set
				continue;
			}
			observed = dfopf[unotother].observed;
			if (lgl->opts->unhdextstamp.val && startstamp <= observed) {
				LOG(1, "stamping failing edge %d %d", lit, other);
				for (failed = lit; dfpr[lglulit(failed)].discovered > observed; failed = dfpr[lglulit(failed)].parent)
					assert(failed);
				LOG(1, "stamping failed literal %d", failed);
				lglpushstk(lgl, units, -failed);
				lgl->stats->unhd.stamp.failed++;
				if (dfpr[unotother].discovered && !dfpr[unotother].finished) {
					LOG(2, "stamping skips edge %d %d after failed literal %d", lit, other, failed);
					work->top--;
					continue;
				}
			}
			if (!dfpr[uother].discovered) {
				dfpr[uother].parent = lit;
				LOG(4, "stamping %d parent %d", other, lit);
				dfpr[uother].root = root;
				LOG(4, "stamping %d root %d", other, root);
				lglpushwtk(lgl, work, PREFIX, other, 0, 0);
			}
		} else if (wrag == AFTER) { // after recursive call
			LOG(2, "stamping edge %d %d after recursion", lit, other);
			uother = lglulit(other);
			ulit = lglulit(lit);
			if (lgl->opts->unhdextstamp.val && !dfpr[uother].finished &&
				dfpr[uother].discovered < dfpr[ulit].discovered) {
				LOG(2, "stamping back edge %d %d", lit, other);
				dfpr[ulit].discovered = dfpr[uother].discovered;
				LOG(3, "stamping %d reduced discovered to %d", lit, dfpr[ulit].discovered);
				if (dfopf[ulit].flag) {
					LOG(2, "stamping %d as being part of a non-trivial SCC", lit);
					dfopf[ulit].flag = 0;
				}
			}
			dfopf[uother].observed = stamp;
			LOG(3, "stamping %d observed %d", other, stamp);
		} else {
			assert(wrag == POSTFIX);
			LOG(2, "stamping postfix %d", lit);
			ulit = lglulit(lit);
			if (dfopf[ulit].flag) {
				stamp++;
				sccsize = 0;
				discovered = dfpr[ulit].discovered;
				do {
					other = lglpopstk(sccs);
					uother = lglulit(other);
					dfopf[uother].pushed = -1;
					dfopf[uother].flag = 0;
					dfpr[uother].discovered = discovered;
					dfpr[uother].finished = stamp;
					LOG(3,
						"stamping %d interval %d %d parent %d root %d",
						other,
						dfpr[uother].discovered,
						dfpr[uother].finished,
						dfpr[uother].parent,
						dfpr[uother].root);
					sccsize++;
				} while (other != lit);
				assert(lgl->opts->unhdextstamp.val || sccsize == 1);
				if (sccsize > 1) {
					LOG(2, "stamping non trivial SCC of size %d", sccsize);
					lgl->stats->unhd.stamp.sumsccsizes += sccsize;
					lgl->stats->unhd.stamp.sccs++;
				}
			} else
				assert(lgl->opts->unhdextstamp.val);
		}
	}
	assert(lglmtwtk(work));
	assert(lglmtstk(sccs));
	(void)trds;
	return stamp;
}

static int
lglunhlca(LGL* lgl, const DFPR* dfpr, int a, int b)
{
	const DFPR *c, *d;
	int u, v, p;
	if (a == b)
		return a;
	u = lglulit(a), v = lglulit(b);
	c = dfpr + u, d = dfpr + v;
	if (c->discovered <= d->discovered) {
		p = a;
	} else {
		assert(c->discovered > d->discovered);
		p = b;
		SWAP(const DFPR*, c, d);
	}
	for (;;) {
		assert(c->discovered <= d->discovered);
		if (d->finished <= c->finished)
			break;
		p = c->parent;
		if (!p)
			break;
		u = lglulit(p);
		c = dfpr + u;
	}
	LOG(3, "unhiding least common ancestor of %d and %d is %d", a, b, p);
	(void)lgl;
	return p;
}

static int
lglunhidefailed(LGL* lgl, const DFPR* dfpr)
{
	int idx, sign, lit, unit, nfailed = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		for (sign = -1; sign <= 1; sign += 2) {
			if (lglterminate(lgl))
				return 0;
			if (!lglsyncunits(lgl))
				return 0;
			INCSTEPS(unhd.steps);
			lit = sign * idx;
			if (lglval(lgl, lit))
				continue;
			if (!dfpr[lglulit(lit)].discovered)
				continue;
			if (lglunhimplincl(dfpr, lit, -lit)) {
				unit = -lit;
				LOG(2, "unhiding %d implies %d", lit, -lit);
			} else if (lglunhimplincl(dfpr, -lit, lit)) {
				unit = lit;
				LOG(2, "unhiding %d implies %d", -lit, lit);
			} else
				continue;
			LOG(1, "unhiding failed literal %d", -unit);
			lglunit(lgl, unit);
			lgl->stats->unhd.failed.lits++;
			nfailed++;
			if (lglbcp(lgl))
				continue;
			LOG(1, "empty clause after propagating unhidden failed literal");
			assert(!lgl->mt);
			lglmt(lgl);
			return 0;
		}
	}
	LOG(1, "unhiding %d failed literals in this round", nfailed);
	return 1;
}

static int
lglunhroot(const DFPR* dfpr, int lit)
{
	return dfpr[lglulit(lit)].root;
}

static int
lglunhidebintrn(LGL* lgl, const DFPR* dfpr, int irronly)
{
	int idx, sign, lit, blit, tag, red, other, other2, unit, root, lca;
	int nbinred, ntrnred, nbinunits, ntrnunits, ntrnstr, ntrnhbrs;
	const int *p, *eow;
	int ulit, uother;
	ptrdiff_t delta;
	int *w, *q;
	HTS* hts;
	nbinred = ntrnred = nbinunits = ntrnunits = ntrnstr = ntrnhbrs = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		for (sign = -1; sign <= 1; sign += 2) {
			if (lglterminate(lgl))
				return 0;
			if (!lglsyncunits(lgl))
				return 0;
			INCSTEPS(unhd.steps);
			lit = sign * idx;
			if (lglval(lgl, lit))
				continue;
			ulit = lglulit(lit);
			if (!dfpr[ulit].discovered)
				continue;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			q = w;
			for (p = w; p < eow; p++) {
				blit = *p;
				*q++ = blit;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					*q++ = *++p;
				if (tag == LRGCS)
					continue;
				red = blit & REDCS;
				other = blit >> RMSHFT;
				if (lglval(lgl, other))
					continue;
				uother = lglulit(other);
				if (tag == BINCS) {
					if (lglunhimplies2(dfpr, other, lit)) {
						LOG(2,
							"unhiding removal of literal %d "
							"with implication %d %d from binary clause %d %d",
							other,
							other,
							lit,
							lit,
							other);
						lgl->stats->unhd.units.bin++;
						nbinunits++;
						unit = lit;
					UNIT:
						lglunit(lgl, unit);
						p++;
						while (p < eow)
							*q++ = *p++;
						lglshrinkhts(lgl, hts, hts->count - (p - q));
						if (lglbcp(lgl))
							goto NEXTIDX;
						LOG(1, "empty clause after propagating unhidden lifted unit");
						assert(!lgl->mt);
						lglmt(lgl);
						return 0;
					} else if ((root = lglunhroot(dfpr, -lit)) && !lglval(lgl, root) &&
							   root == lglunhroot(dfpr, -other)) {
						LOG(2, "negated literals in binary clause %d %d implied by %d", lit, other, root);
						lgl->stats->unhd.failed.bin++;
						lca = lglunhlca(lgl, dfpr, -lit, -other);
						unit = -lca;
						assert(unit);
						goto UNIT;
					} else if (!irronly && !red)
						continue;
					else {
						if (dfpr[uother].parent == -lit)
							continue;
						if (dfpr[ulit].parent == -other)
							continue;
						if (!lglunhimplies2(dfpr, -lit, other))
							continue;
						LOG(2, "unhiding tautological binary clause %d %d", lit, other);
						lgl->stats->unhd.tauts.bin++;
						lgl->stats->prgss++;
						if (red)
							lgl->stats->unhd.tauts.red++;
						nbinred++;
						lglrmbwch(lgl, other, lit, red);
						LOG(2, "removed %s binary clause %d %d", lglred2str(red), lit, other);
						lgldeclscnt(lgl, 2, red, 0);
						assert(!lgl->dense);
						q--;
					}
				} else {
					assert(tag == TRNCS);
					other2 = *p;
					if (lglval(lgl, other2))
						continue;
					if (lglunhimplies2incl(dfpr, other, lit) && lglunhimplies2incl(dfpr, other2, lit)) {
						LOG(2,
							"unhiding removal of literals %d and %d with implications "
							"%d %d and %d %d from ternary clause %d %d %d",
							other,
							other2,
							other,
							lit,
							other2,
							lit,
							lit,
							other,
							other2);
						lgl->stats->unhd.str.trn += 2;
						if (red)
							lgl->stats->unhd.str.red += 2;
						lgl->stats->unhd.units.trn++;
						ntrnunits++;
						unit = lit;
						goto UNIT;
					} else if ((root = lglunhroot(dfpr, -lit)) && !lglval(lgl, root) &&
							   root == lglunhroot(dfpr, -other) && root == lglunhroot(dfpr, -other2)) {
						LOG(2,
							"negation of literals in ternary clauses %d %d %d "
							"implied by %d",
							lit,
							other,
							other2,
							root);
						lgl->stats->unhd.failed.trn++;
						lca = lglunhlca(lgl, dfpr, -lit, -other);
						assert(lca);
						lca = lglunhlca(lgl, dfpr, lca, -other2);
						assert(lca);
						unit = -lca;
						goto UNIT;
					} else if ((red || irronly) &&
							   (lglunhimplies2incl(dfpr, -lit, other) || lglunhimplies2incl(dfpr, -lit, other2))) {

						if (!lgl->opts->unhdatrn.val)
							continue;
						if (red && lgl->opts->unhdatrn.val == 1)
							continue;

						LOG(2, "unhiding %s tautological ternary clause %d %d %d", lglred2str(red), lit, other, other2);
						lgl->stats->unhd.tauts.trn++;
						lgl->stats->prgss++;
						if (red)
							lgl->stats->unhd.tauts.red++;
						ntrnred++;
						lglrmtwch(lgl, other, lit, other2, red);
						lglrmtwch(lgl, other2, lit, other, red);
						LOG(2, "removed %s ternary clause %d %d %d", lglred2str(red), lit, other, other2);
						lgldeclscnt(lgl, 3, red, 0);
						assert(!lgl->dense);
						if (!red && lgl->opts->move.val >= 2) {
							assert(q >= w);
							assert(q[-2] == blit);
							assert(q[-1] == other2);
							q[-2] |= REDCS;
							delta = lglwchtrn(lgl, other, lit, other2, REDCS);
							delta += lglwchtrn(lgl, other2, lit, other, REDCS);
							if (delta)
								w += delta, eow += delta, p += delta, q += delta;
							LOG(2, "moved ternary clause %d %d %d", lit, other, other2);
							lgl->stats->red.trn++, assert(lgl->stats->red.trn > 0);
							lgl->stats->moved.trn++;
						} else
							q -= 2;
					} else if (lglunhimplies2incl(dfpr, other2, lit)) {
					TRNSTR:
						LOG(2,
							"unhiding removal of literal %d with implication "
							"%d %d from ternary clause %d %d %d",
							other2,
							other2,
							lit,
							lit,
							other,
							other2);
						lgl->stats->unhd.str.trn++;
						lgl->stats->prgss++;
						if (red)
							lgl->stats->unhd.str.red++;
						ntrnstr++;
						lglrmtwch(lgl, other, lit, other2, red);
						lglrmtwch(lgl, other2, lit, other, red);
						LOG(2, "removed %s ternary clause %d %d %d", lglred2str(red), lit, other, other2);
						lgldeclscnt(lgl, 3, red, 0);
						if (!red)
							lglincirr(lgl, 2);
						else
							lgl->stats->red.bin++, assert(lgl->stats->red.bin > 0);
						delta = lglwchbin(lgl, other, lit, red);
						if (delta) {
							p += delta, q += delta, eow += delta, w += delta;
						}
						(--q)[-1] = red | BINCS | RMSHFTLIT(other);
						lgldrupligaddclsarg(lgl, REDCS, lit, other, 0);
						continue;
					} else if (lglunhimplies2incl(dfpr, other, lit)) {
						SWAP(int, other, other2);
						goto TRNSTR;
					} else if (lgl->opts->unhdhbr.val && (root = lglunhroot(dfpr, -lit)) && !lglval(lgl, root)) {
						if (root == lglunhroot(dfpr, -other2)) {
							lca = lglunhlca(lgl, dfpr, -lit, -other2);
						} else if (root == lglunhroot(dfpr, -other)) {
							lca = lglunhlca(lgl, dfpr, -lit, -other);
							SWAP(int, other, other2);
						} else if (lglunhimplies2incl(dfpr, root, -other2))
							lca = root;
						else if (lglunhimplies2incl(dfpr, root, -other)) {
							lca = root;
							SWAP(int, other, other2);
						} else
							continue;
						assert(lca);
						if (abs(lca) == abs(lit))
							continue;
						if (abs(lca) == abs(other))
							continue;
						if (abs(lca) == abs(other2))
							continue;
						if (lglunhimplies2incl(dfpr, lca, other))
							continue;
						LOG(2,
							"negations of literals %d %d in ternary clause %d %d %d "
							"implied by %d",
							lit,
							other2,
							lit,
							other,
							other2,
							lca);
						lgl->stats->unhd.hbrs.trn++;
						if (red)
							lgl->stats->unhd.hbrs.red++;
						lgl->stats->prgss++;
						ntrnhbrs++;
						LOG(2, "unhidden hyper binary resolved clause %d %d", -lca, other);
						lgldrupligaddclsarg(lgl, REDCS, -lca, other, 0);
						assert(lca != -lit);
						lgl->stats->red.bin++, assert(lgl->stats->red.bin > 0);
						delta = lglwchbin(lgl, -lca, other, REDCS);
						if (delta) {
							p += delta, q += delta, eow += delta, w += delta;
						}
						delta = lglwchbin(lgl, other, -lca, REDCS);
						if (delta) {
							p += delta, q += delta, eow += delta, w += delta;
						}
					}
				}
			}
			lglshrinkhts(lgl, hts, hts->count - (p - q));
		}
	NEXTIDX:;
	}
	if (nbinred)
		LOG(2, "unhiding %d tautological binary clauses", nbinred);
	if (nbinunits)
		LOG(2, "unhiding %d units in binary clauses", nbinunits);
	if (ntrnred)
		LOG(2, "unhiding %d tautological ternary clauses", ntrnred);
	if (ntrnstr)
		LOG(2, "unhiding %d strengthened ternary clauses", ntrnstr);
	if (ntrnunits)
		LOG(2, "unhiding %d units in ternary clauses", ntrnunits);
	if (ntrnstr)
		LOG(2, "unhidden %d hyper binary resolution from ternary clauses", ntrnhbrs);
	return 1;
}

static int
lglcmpdfl(const DFL* a, const DFL* b)
{
	return a->discovered - b->discovered;
}

static int
lglunhideglue(LGL* lgl, const DFPR* dfpr, int glue, int irronly)
{
	DFL *dfl, *eodfl, *d, *e;
	int szdfl, posdfl, negdfl, ndfl, res;
	int oldsize, newsize, hastobesatisfied, satisfied, tautological;
	int watched, lit, ulit, val, sign, nonfalse, root, lca, unit;
	int ntaut = 0, nstr = 0, nunits = 0, nhbrs = 0, lidx;
	int *p, *q, *c, *eoc, red;
	int lca1, lca2, root1, root2;
	Stk* lits;
#ifndef NLGLOG
	char type[20];
	if (glue < 0)
		sprintf(type, "irredundant");
	else
		sprintf(type, "redundant glue %d", glue);
#endif
	assert(!lgl->mt);
	assert(-1 <= glue && glue < MAXGLUE);
	if (glue < 0) {
		lits = &lgl->irr;
		red = 0;
	} else {
		lits = lgl->red + glue;
		red = REDCS;
	}
	res = 1;
	dfl = 0;
	szdfl = 0;
	// go through all clauses of this glue and for each do:
	//
	//   SHRINK  simplify clause according to current assignment
	//   FAILED  check if it is a hidden failed literal
	//   HTE     check if it is a hidden tautology
	//   STRNEG  remove hidden literals using complement literals
	//   STRPOS  remove hidden literals using positive literals
	//   HBR     perform unhiding hyper binary resolution
	//   NEXT    clean up, unwatch if necessary, reconnect bin/trn, bcp unit
	//
	for (c = lits->start; !lgl->mt && c < lits->top; c = eoc + 1) {
		if (lglterminate(lgl) || !lglsyncunits(lgl)) {
			res = 0;
			break;
		}
		if ((lit = *(eoc = c)) >= NOTALIT)
			continue;
		INCSTEPS(unhd.steps);
		lidx = c - lits->start;
		if (red) {
			lidx <<= GLUESHFT;
			lidx |= glue;
		}
		watched = 1;
		while (*eoc)
			eoc++;
		oldsize = eoc - c;

		unit = hastobesatisfied = satisfied = tautological = ndfl = 0;
		// SHRINK: check satisfied + remove false literals + count visited
		q = c;
		nonfalse = posdfl = negdfl = 0;
		for (p = c; p < eoc; p++) {
			lit = *p;
			val = lglval(lgl, lit);
			if (val > 0) {
				satisfied = 1;
				q = c + 2;
				break;
			}
			if (val < 0) {
				if (p < c + 2) {
					*q++ = lit;			  // watched, so have to keep it
					hastobesatisfied = 1; // for assertion checking only
				}
				continue;
			}
			*q++ = lit;
			nonfalse++;
			if (dfpr[lglulit(lit)].discovered)
				posdfl++; // count pos in BIG
			if (dfpr[lglulit(-lit)].discovered)
				negdfl++; // count neg in BIG
		}
		*(eoc = q) = 0;
		assert(eoc - c >= 2);	// we assume bcp done before
		ndfl = posdfl + negdfl; // number of literals in BIG
		if (hastobesatisfied)
			assert(satisfied);
		if (satisfied || ndfl < 2)
			goto NEXT;
		assert(nonfalse == eoc - c);
		assert(nonfalse >= negdfl);
		// FAILED: find root implying all negated literals
		if (nonfalse != negdfl)
			goto HTE; // not enough complement lits in BIG
		assert(c < eoc);
		root = lglunhroot(dfpr, -*c);
		if (lglval(lgl, root))
			goto HTE;
		for (p = c + 1; p < eoc && lglunhroot(dfpr, -*p) == root; p++)
			;
		if (p < eoc)
			goto HTE; // at least two roots
		LOGCLS(2, c, "unhiding failed literal through large %s clause", type);
		LOG(2, "unhiding that all negations are implied by root %d", root);
		lca = -*c;
		for (p = c + 1; p < eoc; p++)
			lca = lglunhlca(lgl, dfpr, -*p, lca);
		assert(!lglval(lgl, lca));
		LOG(2, "unhiding failed large %s clause implied by LCA %d", type, lca);
		lgl->stats->unhd.failed.lrg++;
		unit = -lca;
		goto NEXT; // otherwise need to properly unwatch and restart etc.
				   // which is too difficult to implement so leave further
				   // simplification of this clause to next unhiding round
	HTE:
		if (glue < 0 && !irronly)
			goto STRNEG; // skip tautology checking if
		// redundant clauses used and
		// we work on irredundant clauses
		if (posdfl < 2 || negdfl < 2)
			goto STRNEG;
		if (ndfl > szdfl) {
			RSZ(dfl, szdfl, ndfl);
			szdfl = ndfl;
		}
		ndfl = 0;
		// copy all literals and their complements to 'dfl'
		for (p = c; p < eoc; p++) {
			for (sign = -1; sign <= 1; sign += 2) {
				lit = *p;
				ulit = lglulit(sign * lit);
				if (!dfpr[ulit].discovered)
					continue; // not in BIG so skip
				assert(ndfl < szdfl);
				dfl[ndfl].discovered = dfpr[ulit].discovered;
				dfl[ndfl].finished = dfpr[ulit].finished;
				dfl[ndfl].sign = sign;
#ifndef NLGLOG
				dfl[ndfl].lit4logging = lit;
#endif
				ndfl++;
			}
		}
		ADDSTEPS(unhd.steps, 6); // rough guess
		SORT(DFL, dfl, ndfl, lglcmpdfl);
		eodfl = dfl + ndfl;
		// Now check if there are two consecutive literals, the first
		// a complement of a literal in the clause, which implies another
		// literal actually occurring positive in the clause, where
		// 'd' ranges over complement literals
		// 'e' ranges over original literals
		for (d = dfl; d < eodfl - 1; d++)
			if (d->sign < 0)
				break; // get first negative pos
		while (d < eodfl - 1) {
			assert(d->sign < 0);
			for (e = d + 1; e < eodfl && e->finished < d->finished; e++) {
				if (e->sign < 0)
					continue; // get next positive pos
				assert(d->sign < 0 && e->sign > 0);
				assert(d->discovered <= e->discovered);
				assert(e->finished <= d->finished);
				LOGCLS(2,
					   c,
					   "unhiding with implication %d %d tautological %s clause",
					   -d->lit4logging,
					   e->lit4logging,
					   type);
				ntaut++;
				lgl->stats->unhd.tauts.lrg++;
				if (red)
					lgl->stats->unhd.tauts.red++;
				lgl->stats->prgss++;
				tautological = 1;
				goto NEXT;
			}
			for (d = e; d < eodfl && d->sign > 0; d++)
				;
		}
	STRNEG:
		if (negdfl < 2)
			goto STRPOS;
		if (negdfl > szdfl) {
			RSZ(dfl, szdfl, negdfl);
			szdfl = negdfl;
		}
		INCSTEPS(unhd.steps);
		ndfl = 0;
		// copy complement literals to 'dfl'
		for (p = c; p < eoc; p++) {
			lit = *p;
			ulit = lglulit(-lit); // NOTE: '-lit' not 'lit'
			if (!dfpr[ulit].discovered)
				continue;
			assert(ndfl < szdfl);
			dfl[ndfl].discovered = dfpr[ulit].discovered; // of '-lit'
			dfl[ndfl].finished = dfpr[ulit].finished;	  // of '-lit'
			dfl[ndfl].lit = lit;						  // NOTE: but 'lit' here
			ndfl++;
		}
		if (ndfl < 2)
			goto STRPOS;
		ADDSTEPS(unhd.steps, 3); // rough guess
		SORT(DFL, dfl, ndfl, lglcmpdfl);
		eodfl = dfl + ndfl;
		for (d = dfl; d < eodfl - 1; d = e) {
			assert(d->discovered);
			for (e = d + 1; e < eodfl && d->finished >= e->finished; e++) {
				assert(d->discovered <= e->discovered);
				lit = e->lit;
				LOGCLS(2,
					   c,
					   "unhiding removal of literal %d "
					   "with implication %d %d from large %s clause",
					   lit,
					   -d->lit,
					   -e->lit,
					   type);
				e->lit = 0;
				nstr++;
				lgl->stats->unhd.str.lrg++;
				if (red)
					lgl->stats->unhd.str.red++;
				lgl->stats->prgss++;
				if (!watched)
					continue;
				if (lit != c[0] && lit != c[1])
					continue;
				lglrmlwch(lgl, c[0], red, lidx);
				lglrmlwch(lgl, c[1], red, lidx);
				watched = 0;
			}
		}
		assert(eoc - c >= 1);
		q = c;
		if (watched)
			q += 2; // keep watched literals
					// if we still watch
		// move non BIG literals
		for (p = q; p < eoc; p++) {
			lit = *p;
			ulit = lglulit(-lit); // NOTE: '-lit' not 'lit'
			if (dfpr[ulit].discovered)
				continue;
			*q++ = lit;
		}
		// copy from 'dfl' unremoved BIG literals back to clause
		for (d = dfl; d < eodfl; d++) {
			lit = d->lit;
			if (!lit)
				continue;
			if (watched && lit == c[0])
				continue;
			if (watched && lit == c[1])
				continue;
			*q++ = lit;
		}
		*(eoc = q) = 0;
	STRPOS:
		if (posdfl < 2)
			goto HBR;
		if (posdfl > szdfl) {
			RSZ(dfl, szdfl, posdfl);
			szdfl = posdfl;
		}
		ndfl = 0;
		// copy original literals to 'dfl' but sort reverse wrt 'discovered'
		for (p = c; p < eoc; p++) {
			lit = *p;
			ulit = lglulit(lit); // NOTE: now 'lit'
			if (!dfpr[ulit].discovered)
				continue;
			assert(ndfl < szdfl);
			dfl[ndfl].discovered = -dfpr[ulit].discovered; // of 'lit'
			dfl[ndfl].finished = -dfpr[ulit].finished;	   // of 'lit'
			dfl[ndfl].lit = lit;
			ndfl++;
		}
		if (ndfl < 2)
			goto NEXT;
		ADDSTEPS(unhd.steps, 3); // rough guess
		SORT(DFL, dfl, ndfl, lglcmpdfl);
		eodfl = dfl + ndfl;
		for (d = dfl; d < eodfl - 1; d = e) {
			assert(d->discovered);
			for (e = d + 1; e < eodfl && d->finished >= e->finished; e++) {
				assert(d->discovered <= e->discovered);
				lit = e->lit;
				LOGCLS(2,
					   c,
					   "unhiding removal of literal %d "
					   "with implication %d %d from large %s clause",
					   lit,
					   e->lit,
					   d->lit,
					   type);
				e->lit = 0;
				nstr++;
				lgl->stats->unhd.str.lrg++;
				if (red)
					lgl->stats->unhd.str.red++;
				lgl->stats->prgss++;
				if (!watched)
					continue;
				if (lit != c[0] && lit != c[1])
					continue;
				lglrmlwch(lgl, c[0], red, lidx);
				lglrmlwch(lgl, c[1], red, lidx);
				watched = 0;
			}
		}
		assert(eoc - c >= 1);
		q = c;
		if (watched)
			q += 2;
		for (p = q; p < eoc; p++) {
			lit = *p;
			ulit = lglulit(lit); // NOTE: now 'lit'
			if (dfpr[ulit].discovered)
				continue;
			*q++ = lit;
		}
		for (d = dfl; d < eodfl; d++) {
			lit = d->lit;
			if (!lit)
				continue;
			if (watched && lit == c[0])
				continue;
			if (watched && lit == c[1])
				continue;
			*q++ = lit;
		}
		*(eoc = q) = 0;
	HBR:
		if (!lgl->opts->unhdhbr.val)
			goto NEXT;
		if (eoc - c < 3)
			goto NEXT;
		root1 = root2 = lca1 = lca2 = 0;
		for (p = c; (lit = *p); p++) {
			root = lglunhroot(dfpr, -lit);
			if (!root)
				root = -lit;
			if (!root1) {
				root1 = root;
				continue;
			}
			if (root1 == root)
				continue;
			if (!root2) {
				root2 = root;
				continue;
			}
			if (root2 == root)
				continue;
			if (lglunhimplies2incl(dfpr, root1, -lit)) {
				lca1 = root1;
				continue;
			}
			if (lglunhimplies2incl(dfpr, root2, -lit)) {
				lca2 = root2;
				continue;
			}
			goto NEXT; // else more than two roots ...
		}
		assert(root1);
		if (!root2)
			goto NEXT;
		if (root1 == -root2)
			goto NEXT;
		if (lglunhimplies2incl(dfpr, root1, -root2))
			goto NEXT;
		LOGCLS(2, c, "root hyper binary resolution %d %d of %s clause", -root1, -root2, type);
		if (!lca1 && !lca2) {
			for (p = c; (lit = *p); p++) {
				root = lglunhroot(dfpr, -lit);
				if (root) {
					if (root == root1)
						lca1 = lca1 ? lglunhlca(lgl, dfpr, lca1, -lit) : -lit;
					if (root == root2)
						lca2 = lca2 ? lglunhlca(lgl, dfpr, lca2, -lit) : -lit;
				} else {
					assert(!lca2);
					if (lca1)
						lca2 = -lit;
					else
						lca1 = -lit;
				}
			}
		} else {
			if (!lca1)
				lca1 = root1;
			if (!lca2)
				lca2 = root2;
		}
		if (lca1 == -lca2)
			goto NEXT;
		if (lglunhimplies2incl(dfpr, lca1, -lca2))
			goto NEXT;
		LOGCLS(2, c, "lca hyper binary resolution %d %d of %s clause", -lca1, -lca2, type);
		lgl->stats->unhd.hbrs.lrg++;
		if (red)
			lgl->stats->unhd.hbrs.red++;
		lgldrupligaddclsarg(lgl, REDCS, -lca1, -lca2, 0);
		lglwchbin(lgl, -lca1, -lca2, REDCS);
		lglwchbin(lgl, -lca2, -lca1, REDCS);
		lgl->stats->red.bin++;
		assert(lgl->stats->red.bin > 0);
	NEXT:
		newsize = eoc - c;
		assert(satisfied || tautological || newsize >= 1);
		if (newsize <= 3 || satisfied || tautological) {
			lgldeclscnt(lgl, oldsize, red, glue);
			if (watched) {
				lglrmlwch(lgl, c[0], red, lidx);
				lglrmlwch(lgl, c[1], red, lidx);
			}
		} else if (!red) {
			assert(lgl->stats->irr.lits.cur >= c + oldsize - eoc);
			lgl->stats->irr.lits.cur -= c + oldsize - eoc;
		}
		assert(!*eoc);
		for (p = c + oldsize; p > eoc; p--)
			*p = REMOVED;
		if (satisfied || tautological) {
			while (p >= c)
				*p-- = REMOVED;
			if (red) {
				LGLCHKACT(c[-1]);
				c[-1] = REMOVED;
			}
			eoc = c + oldsize;
			continue;
		}
		if (newsize < oldsize)
			lgldrupligaddclsaux(lgl, REDCS, c);
		if (red && newsize <= 3) {
			LGLCHKACT(c[-1]);
			c[-1] = REMOVED;
		}
		if (newsize > 3 && !watched) {
			(void)lglwchlrg(lgl, c[0], c[1], red, lidx);
			(void)lglwchlrg(lgl, c[1], c[0], red, lidx);
		} else if (newsize == 3) {
			LOGCLS(2, c, "large %s clause became ternary", type);
			lglwchtrn(lgl, c[0], c[1], c[2], red);
			lglwchtrn(lgl, c[1], c[0], c[2], red);
			lglwchtrn(lgl, c[2], c[0], c[1], red);
			if (!red)
				lglincirr(lgl, 3);
			else
				lgl->stats->red.trn++, assert(lgl->stats->red.trn > 0);
			c[0] = c[1] = c[2] = *eoc = REMOVED;
		} else if (newsize == 2) {
			LOGCLS(2, c, "large %s clause became binary", type);
			lglwchbin(lgl, c[0], c[1], red);
			lglwchbin(lgl, c[1], c[0], red);
			if (!red)
				lglincirr(lgl, 2);
			else
				lgl->stats->red.bin++, assert(lgl->stats->red.bin > 0);
			c[0] = c[1] = *eoc = REMOVED;
		} else if (newsize == 1) {
			LOGCLS(2, c, "large %s clause became unit", type);
			unit = c[0]; // even works if unit already set
			c[0] = *eoc = REMOVED;
			lgl->stats->unhd.units.lrg++;
			nunits++;
		}
		if (!unit)
			continue;
		lglunit(lgl, unit);
		if (lglbcp(lgl))
			continue;
		assert(!lgl->mt);
		lglmt(lgl);
		LOG(1, "unhiding large clause produces empty clause");
		res = 0;
	}
	if (nunits)
		LOG(1, "unhiding %d units from large %s clauses", nunits, type);
	if (ntaut)
		LOG(1, "unhiding %d large tautological %s clauses", ntaut, type);
	if (nstr)
		LOG(1, "unhiding removal of %d literals in %s clauses", nstr, type);
	if (nhbrs)
		LOG(1, "unhiding %d hyper binary resolutions in %s clauses", nhbrs, type);
	if (dfl)
		DEL(dfl, szdfl);
	return res;
}

static void
lglfixlrgwchs(LGL* lgl)
{
	int idx, sign, lit, blit, tag, red, lidx, fixed;
	const int *p, *eow, *c;
	int *q, *w;
	HTS* hts;
	fixed = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			q = w;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == BINCS) {
					*q++ = blit;
					continue;
				}
				lidx = *++p;
				if (tag == TRNCS) {
					*q++ = blit;
					*q++ = lidx;
					continue;
				}
				assert(tag == LRGCS);
				red = blit & REDCS;
				c = lglidx2lits(lgl, red, lidx);
				if (*c >= NOTALIT) {
					fixed++;
					continue;
				}
				*q++ = blit;
				*q++ = lidx;
			}
			lglshrinkhts(lgl, hts, hts->count - (p - q));
		}
	}
	assert(!(fixed & 1));
	if (fixed)
		LOG(1, "fixed %d large watches", fixed);
}

static int
lglunhidelrg(LGL* lgl, const DFPR* dfpr, int irronly)
{
	int glue, res = 1;
	for (glue = -1; res && glue < MAXGLUE; glue++)
		res = lglunhideglue(lgl, dfpr, glue, irronly);
	lglfixlrgwchs(lgl);
	return res;
}

static int
lglunhdunits(LGL* lgl)
{
	int res = lgl->stats->unhd.units.bin;
	res += lgl->stats->unhd.units.trn;
	res += lgl->stats->unhd.units.lrg;
	return res;
}

static int
lglunhdfailed(LGL* lgl)
{
	int res = lgl->stats->unhd.stamp.failed;
	res += lgl->stats->unhd.failed.lits;
	res += lgl->stats->unhd.failed.bin;
	res += lgl->stats->unhd.failed.trn;
	res += lgl->stats->unhd.failed.lrg;
	return res;
}

static int
lglunhdhbrs(LGL* lgl)
{
	int res = lgl->stats->unhd.hbrs.trn;
	res += lgl->stats->unhd.hbrs.lrg;
	return res;
}

static int
lglunhdtauts(LGL* lgl)
{
	int res = lgl->stats->unhd.stamp.trds;
	res += lgl->stats->unhd.tauts.bin;
	res += lgl->stats->unhd.tauts.trn;
	res += lgl->stats->unhd.tauts.lrg;
	return res;
}

static int
lglunhdstrd(LGL* lgl)
{
	int res = lgl->stats->unhd.units.bin; // shared!
	res += lgl->stats->unhd.str.trn;
	res += lgl->stats->unhd.str.lrg;
	return res;
}

static void
lglrmbindup(LGL* lgl)
{
	int idx, sign, lit, blit, tag, red, other, round, redrem, irrem;
	int *w, *eow, *p, *q;
	HTS* hts;
	redrem = irrem = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			assert(lglmtstk(&lgl->seen));
			for (round = 0; round < 2; round++) {
				hts = lglhts(lgl, lit);
				w = lglhts2wchs(lgl, hts);
				eow = w + hts->count;
				q = w;
				for (p = w; p < eow; p++) {
					blit = *p;
					tag = blit & MASKCS;
					if (tag != BINCS)
						*q++ = blit;
					if (tag == LRGCS || tag == TRNCS)
						*q++ = *++p;
					if (tag != BINCS)
						continue;
					red = blit & REDCS;
					other = blit >> RMSHFT;
					if (lglsignedmarked(lgl, other)) {
						if (round && !red)
							goto ONLYCOPY;
						if (red)
							redrem++;
						else
							irrem++;
						if (abs(lit) > abs(other)) {
							LOG(2,
								"removing duplicated %s binary clause %d %d and 2nd watch %d",
								lglred2str(red),
								lit,
								other,
								other);
							lgldeclscnt(lgl, 2, red, 0);
							if (!red && lgl->dense)
								lgldecocc(lgl, lit), lgldecocc(lgl, other);
							lgl->stats->bindup.removed++;
							if (red)
								lgl->stats->bindup.red++;
						} else
							LOG(2,
								"removing 1st watch %d of duplicated %s binary clause %d %d",
								other,
								lglred2str(red),
								other,
								lit);
					} else {
						if ((!round && !red) || (round && red))
							lglsignedmarknpushseen(lgl, other);
					ONLYCOPY:
						*q++ = blit;
					}
				}
				lglshrinkhts(lgl, hts, hts->count - (p - q));
			}
			lglpopnunmarkstk(lgl, &lgl->seen);
		}
	}
	assert(!(irrem & 1));
	assert(!(redrem & 1));
}

static DFPR*
lglstampall(LGL* lgl, int irronly)
{
	int roots, searches, noimpls, unassigned, visited;
	unsigned pos, delta, mod, ulit, first, last, count;
	int root, stamp, rootsonly, lit;
	Stk units, sccs, trds;
	DFOPF *dfopf, *q;
	DFPR* dfpr;
	Wtk work;
	Val val;
	if (lgl->nvars <= 2)
		return 0;
	lglrmbindup(lgl);
	NEW(dfpr, 2 * lgl->nvars);
	NEW(dfopf, 2 * lgl->nvars);
	CLR(work);
	CLR(sccs);
	CLR(trds);
	CLR(units);
	for (q = dfopf; q < dfopf + 2 * lgl->nvars; q++)
		q->pushed = -1;
	searches = roots = noimpls = unassigned = stamp = visited = 0;
	for (rootsonly = 1; rootsonly >= 0; rootsonly--) {
		count = 0;
		first = mod = 2 * (lgl->nvars - 2);
		assert(mod > 0);
		pos = lglrand(lgl) % mod;
		delta = lglrand(lgl) % mod;
		if (!delta)
			delta++;
		while (lglgcd(delta, mod) > 1)
			if (++delta == mod)
				delta = 1;
		LOG(2, "unhiding %s round start %u delta %u mod %u", (rootsonly ? "roots-only" : "non-root"), pos, delta, mod);
		for (;;) {
			if (lglterminate(lgl)) {
				searches = 0;
				goto DONE;
			}
			if (!lglsyncunits(lgl)) {
				assert(lgl->mt);
				goto DONE;
			}
			ulit = pos + 4;
			root = lglilit(ulit);
			INCSTEPS(unhd.steps);
			count++;
			if (lglval(lgl, root))
				goto CONTINUE;
			if (rootsonly)
				unassigned++;
			if (dfpr[lglulit(root)].discovered)
				goto CONTINUE;
			if (rootsonly && !lglunhdisroot(lgl, root, dfpr, irronly))
				goto CONTINUE;
			if (!lglunhdhasbins(lgl, dfpr, -root, irronly)) {
				if (rootsonly)
					noimpls++;
				goto CONTINUE;
			}
			if (rootsonly)
				roots++;
			searches++;
			assert(lglmtstk(&units));
			stamp = lglstamp(lgl, root, dfpr, dfopf, &work, &units, &sccs, &trds, &visited, stamp, irronly);
			while (!lglmtstk(&units)) {
				lit = lglpopstk(&units);
				val = lglval(lgl, lit);
				if (val > 0)
					continue;
				if (val < 0) {
					assert(!lgl->mt);
					LOG(1, "unhidding stamp unit %d already false", lit);
					lglmt(lgl);
					goto DONE;
				}
				lglunit(lgl, lit);
				if (!lglbcp(lgl)) {
					assert(!lgl->mt);
					LOG(1, "propagating unhidden stamp unit %d failed", lit);
					lglmt(lgl);
					goto DONE;
				}
			}
		CONTINUE:
			last = pos;
			pos += delta;
			if (pos >= mod)
				pos -= mod;
			if (pos == first) {
				assert(count == mod);
				break;
			}
			if (mod == 1)
				break;
			if (first == mod)
				first = last;
		}
	}
	assert(searches >= roots);
	lglprt(lgl,
		   2,
		   "[unhd-%d-%d] %d unassigned variables out of %d (%.0f%%)",
		   lgl->stats->unhd.count,
		   lgl->stats->unhd.rounds,
		   lgl->unassigned,
		   lgl->nvars - 2,
		   lglpcnt(lgl->unassigned, lgl->nvars - 2));
	lglprt(lgl,
		   2,
		   "[unhd-%d-%d] %d root literals out of %d (%.0f%%)",
		   lgl->stats->unhd.count,
		   lgl->stats->unhd.rounds,
		   roots,
		   unassigned,
		   lglpcnt(roots, unassigned));
	lglprt(lgl,
		   2,
		   "[unhd-%d-%d] %d additional non-root searches out of %d (%.0f%%)",
		   lgl->stats->unhd.count,
		   lgl->stats->unhd.rounds,
		   searches - roots,
		   unassigned,
		   lglpcnt(searches - roots, unassigned));
	lglprt(lgl,
		   2,
		   "[unhd-%d-%d] %d literals not in F2 out of %d (%.0f%%)",
		   lgl->stats->unhd.count,
		   lgl->stats->unhd.rounds,
		   noimpls,
		   unassigned,
		   lglpcnt(noimpls, unassigned));
	lglprt(lgl,
		   2,
		   "[unhd-%d-%d] %d visited literals out of %d (%.0f%%)",
		   lgl->stats->unhd.count,
		   lgl->stats->unhd.rounds,
		   visited,
		   unassigned,
		   lglpcnt(visited, unassigned));
	lglprt(lgl,
		   2,
		   "[unhd-%d-%d] %.2f average number visited literals per search",
		   lgl->stats->unhd.count,
		   lgl->stats->unhd.rounds,
		   lglavg(visited, searches));
DONE:
	if (!searches || lgl->mt) {
		DEL(dfpr, 2 * lgl->nvars);
		dfpr = 0;
	}
	lglrelwtk(lgl, &work);
	lglrelstk(lgl, &units);
	lglrelstk(lgl, &sccs);
	lglrelstk(lgl, &trds);
	DEL(dfopf, 2 * lgl->nvars);
	return dfpr;
}

static void
lglsetunhdlim(LGL* lgl)
{
	int64_t limit, irrlim;
	int pen, szpen;
	limit = (lgl->opts->unhdreleff.val * lglvisearch(lgl)) / 1000;
	if (limit < lgl->opts->unhdmineff.val)
		limit = lgl->opts->unhdmineff.val;
	if (lgl->opts->unhdmaxeff.val >= 0 && limit > lgl->opts->unhdmaxeff.val)
		limit = lgl->opts->unhdmaxeff.val;
	limit >>= (pen = lgl->limits->unhd.pen + (szpen = lglszpenaux(lgl, 1, 1)));
	irrlim = (lgl->stats->irr.clauses.cur / 16) >> szpen;
	if (lgl->opts->irrlim.val && limit < irrlim) {
		limit = irrlim;
		lglprt(lgl,
			   1,
			   "[unhide-%d] limit %lld based on %d irredundant clauses penalty %d",
			   lgl->stats->unhd.count,
			   (LGLL)limit,
			   lgl->stats->irr.clauses.cur,
			   szpen);
	} else
		lglprt(lgl,
			   1,
			   "[unhide-%d] limit %lld with penalty %d = %d + %d",
			   lgl->stats->unhd.count,
			   (LGLL)limit,
			   pen,
			   lgl->limits->unhd.pen,
			   szpen);
	lgl->limits->unhd.steps = lgl->stats->unhd.steps + limit;
}

static int
lglunhide(LGL* lgl)
{
	int irronly, round, maxrounds, noprgssrounds, success;
	int oldunits, oldfailed, oldtauts, oldhbrs, oldstrd;
	int deltaunits, deltafailed;
	int64_t roundprgss = 0;
	DFPR* dfpr = 0;
	assert(lgl->opts->unhide.val);
	if (lgl->nvars <= 2)
		return 1;
	lgl->stats->unhd.count++;
	assert(!lgl->unhiding);
	lgl->unhiding = 1;
	assert(!lgl->simp);
	lgl->simp = 1;
	lglstart(lgl, &lgl->times->unhide);
	irronly = !lgl->stats->red.bin || (lgl->stats->unhd.count & 1);
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	maxrounds = lgl->opts->unhdroundlim.val;
	lglsetunhdlim(lgl);
	oldunits = lglunhdunits(lgl);
	oldfailed = lglunhdfailed(lgl);
	oldtauts = lglunhdtauts(lgl);
	oldhbrs = lglunhdhbrs(lgl);
	oldstrd = lglunhdstrd(lgl);
	noprgssrounds = round = 0;
	while (!lgl->mt) {
		if (round >= maxrounds)
			break;
		if (round > 0 && roundprgss == lgl->stats->prgss && noprgssrounds++ == lgl->opts->unhdlnpr.val) {
			LOG(1, "too many non progress unhiding rounds");
			break;
		}
		round++;
		roundprgss = lgl->stats->prgss;
		lgl->stats->unhd.rounds++;
		lglgc(lgl);
		if (!lgl->nvars || lgl->mt)
			break;
		assert(!dfpr);
		dfpr = lglstampall(lgl, irronly);
		if (!dfpr)
			break;
		if (!lglunhidefailed(lgl, dfpr))
			break;
		if (!lglunhidebintrn(lgl, dfpr, irronly))
			break;
		if (!lglunhidelrg(lgl, dfpr, irronly))
			break;
		if (lgl->stats->unhd.steps >= lgl->limits->unhd.steps)
			break;
		irronly = !lgl->stats->red.bin || !irronly;
		DEL(dfpr, 2 * lgl->nvars);
		assert(!dfpr);
	}
	if (dfpr)
		DEL(dfpr, 2 * lgl->nvars);
	lglprt(lgl,
		   1,
		   "[unhide-%d-%d] %d units, %d failed, %d tauts, %d hbrs, %d literals",
		   lgl->stats->unhd.count,
		   lgl->stats->unhd.rounds,
		   (deltaunits = lglunhdunits(lgl) - oldunits),
		   (deltafailed = lglunhdfailed(lgl) - oldfailed),
		   lglunhdtauts(lgl) - oldtauts,
		   lglunhdhbrs(lgl) - oldhbrs,
		   lglunhdstrd(lgl) - oldstrd);
	success = deltaunits + deltafailed;
	LGLUPDPEN(unhd, success);
	assert(lgl->simp);
	lgl->simp = 0;
	assert(lgl->unhiding);
	lgl->unhiding = 0;
	lglrep(lgl, 2, 'u');
	lglstop(lgl);
	return !lgl->mt;
}

static int
lglpar64(uint64_t i)
{
	unsigned x;
	int res = 0;
	for (x = i; x; x = x & (x - 1))
		res = !res;
	return res;
}

static uint64_t
lgldec64(uint64_t i)
{
	uint64_t res;
	for (res = i - 1; lglpar64(res); res--)
		;
	return res;
}

static void
lglgdump(LGL* lgl)
{
#ifndef NLGLOG
	const int *p, *start, *top;
	start = lgl->gauss->xors.start;
	top = lgl->gauss->xors.top;
	for (p = start; p < top; p++) {
		if (*p >= NOTALIT)
			continue;
		fprintf(lgl->out, "c eqn[%lld]", (LGLL)(p - start));
		while (*p > 1)
			fprintf(lgl->out, " %d", *p++);
		fprintf(lgl->out, " = %d\n", *p);
	}
#endif
	(void)lgl;
}

static int
lglgaussubclsaux(LGL* lgl, uint64_t signs, const int* c)
{
	int lit, i, min, minocc, tmpocc, blit, tag, other, other2, red, lidx;
	const int *p, *w, *eow, *d, *q;
	HTS* hts;
	assert(lgl->dense);
	minocc = INT_MAX;
	min = i = 0;
	INCSTEPS(gauss.steps.extr);
	for (p = c; (lit = *p); p++) {
		if (lglmarked(lgl, lit))
			return 0;
		assert((size_t)i < 8 * sizeof signs);
		if (signs & (1ull << i++))
			lit = -lit;
		lglsignedmark(lgl, lit);
		tmpocc = lglocc(lgl, lit) + lglhts(lgl, lit)->count;
		assert(tmpocc < INT_MAX);
		if (tmpocc >= minocc)
			continue;
		minocc = tmpocc;
		min = lit;
	}
	assert(min);
	hts = lglhts(lgl, min);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow; p++) {
		INCSTEPS(gauss.steps.extr);
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == LRGCS)
			continue;
		if (tag == BINCS) {
			other = blit >> RMSHFT;
			if (lglsignedmarked(lgl, other))
				return 1;
		} else if (tag == TRNCS) {
			other = blit >> RMSHFT;
			if (!lglsignedmarked(lgl, other))
				continue;
			other2 = *p;
			if (lglsignedmarked(lgl, other2))
				return 1;
		} else {
			assert(tag == OCCS);
			red = blit & REDCS;
			lidx = blit >> RMSHFT;
			d = lglidx2lits(lgl, red, lidx);
			for (q = d; (other = *q); q++)
				if (!lglsignedmarked(lgl, other))
					break;
			if (!other)
				return 1;
		}
	}
	return 0;
}

static int
lglgaussubcls(LGL* lgl, uint64_t signs, const int* c)
{
	int res = lglgaussubclsaux(lgl, signs, c), lit;
	const int* p;
	for (p = c; (lit = *p); p++)
		lglunmark(lgl, lit);
	return res;
}

static void
lglgaussextractedxorincstats(LGL* lgl, int size)
{
	lgl->stats->gauss.arity.sum.total += size;
	if (lgl->stats->gauss.arity.max.total < size)
		lgl->stats->gauss.arity.max.total = size;
	if (lgl->stats->gauss.arity.max.last < size)
		lgl->stats->gauss.arity.max.last = size;
	lgl->stats->gauss.extracted.total++;
	lgl->stats->gauss.extracted.last++;
}

#define GL 2

static int
lglgaussextractxoraux(LGL* lgl, const int* c)
{
	int lit, val, size, maxsize, negs, start, max, *d, *q;
	int allxors = lgl->opts->gaussextrall.val;
	uint64_t signs;
	const int* p;
	assert(!lgl->level);
	maxsize = lgl->opts->gaussmaxor.val;
	max = negs = size = 0;
	start = lglcntstk(&lgl->gauss->xors);
	for (p = c; (lit = *p); p++) {
		if ((val = lglval(lgl, lit)) > 0)
			return 0;
		if (val < 0)
			continue;
		if (lit < 0) {
			if (!allxors && negs)
				return 0;
			negs = !negs;
		}
		if (!max || abs(max) < abs(lit))
			max = lit;
		lglpushstk(lgl, &lgl->gauss->xors, lit);
		if (++size > maxsize)
			return 0;
	}
	assert(negs == 0 || negs == 1);
	if (size <= 1)
		return 0;
	if (!allxors && negs && max > 0)
		return 0;
	lglpushstk(lgl, &lgl->gauss->xors, 0);
	d = lgl->gauss->xors.start + start;
	assert((size_t)size <= 8 * sizeof signs);
	signs = lgldec64(1ull << size);
	do {
		if (!lglgaussubcls(lgl, signs, d))
			break;
		signs = lgldec64(signs);
	} while (signs && lgl->stats->gauss.steps.extr < lgl->limits->gauss.steps.extr);
	if (signs)
		return 0;
	for (q = d; (lit = *q); q++)
		*q = abs(lit);
	*q = !negs;
	LOGEQN(GL, start, "extracted %d-ary XOR constraint", size);
	lglgaussextractedxorincstats(lgl, size);
	return 1;
}

static int
lglgaussextractexactly1(LGL* lgl, const int* c)
{
	const int *p, *q;
	int l, k, size, par;
#ifndef NLGLOG
	int start = lglcntstk(&lgl->gauss->xors);
#endif
	if (!lgl->opts->gausscardweak.val)
		return 0;
	for (p = c; (l = *p); p++) {
		if (lgl->stats->gauss.steps.extr >= lgl->limits->gauss.steps.extr)
			return 0;
		for (q = p + 1; (k = *q); q++) {
			INCSTEPS(gauss.steps.extr);
			if (!lglhasbin(lgl, -l, -k))
				return 0;
		}
	}
	par = 1;
	size = p - c;
	for (p = c; (l = *p); p++) {
		if (l < 0)
			l = -l, par = !par;
		lglpushstk(lgl, &lgl->gauss->xors, l);
	}
	lglpushstk(lgl, &lgl->gauss->xors, par);
	LOGEQN(GL, start, "extracted %d-ary exactly 1 constraint", size);
	lglgaussextractedxorincstats(lgl, size);
	lgl->stats->gauss.arity.sum.exactly1 += size;
	lgl->stats->gauss.extracted.exactly1++;
	return 1;
}

static int
lglgaussextractxor(LGL* lgl, const int* c)
{
	int res;
	if (lglgaussextractexactly1(lgl, c))
		res = 1;
	else {
		int old = lglcntstk(&lgl->gauss->xors);
		if (!(res = lglgaussextractxoraux(lgl, c)))
			lglrststk(&lgl->gauss->xors, old);
	}
	return res;
}

static int
lglgaussextractsmallit(LGL* lgl, int lit)
{
	int allxors = lgl->opts->gaussextrall.val;
	int cls[4], blit, tag, other, other2;
	const int *w, *eow, *p;
	HTS* hts;
	if (lgl->stats->gauss.steps.extr >= lgl->limits->gauss.steps.extr)
		return 0;
	if (lglval(lgl, lit) > 0)
		return 1;
	hts = lglhts(lgl, lit);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == OCCS || tag == LRGCS)
			continue;
		other = blit >> RMSHFT;
		if (!allxors && abs(other) < lit)
			continue;
		cls[0] = lit;
		cls[1] = other;
		if (tag == TRNCS) {
			other2 = *p;
			if (!allxors && abs(other2) < lit)
				continue;
			cls[2] = other2;
			cls[3] = 0;
		} else {
			assert(tag == BINCS);
			cls[2] = 0;
		}
		lglgaussextractxor(lgl, cls);
	}
	return 1;
}

static int
lglgaussextractsmall(LGL* lgl)
{
	int64_t before = lgl->stats->gauss.extracted.total, after, delta;
	int res;
	lglrandlitrav(lgl, lglgaussextractsmallit);
	after = lgl->stats->gauss.extracted.total;
	delta = after - before;
	res = (delta > INT_MAX) ? INT_MAX : delta;
	return res;
}

static int
lglgaussextractlarge(LGL* lgl)
{
	const int *p, *c;
	int res = 0;
	for (c = lgl->irr.start; c < lgl->irr.top; c = p + 1) {
		if (lgl->stats->gauss.steps.extr >= lgl->limits->gauss.steps.extr)
			break;
		if (*(p = c) >= NOTALIT)
			continue;
		res += lglgaussextractxor(lgl, c);
		while (*p)
			p++;
	}
	return res;
}

static void
lglgaussconeqn(LGL* lgl, int eqn)
{
	const int* xors = lgl->gauss->xors.start;
	int i, var;
	INCSTEPS(gauss.steps.elim);
	for (i = eqn; (var = xors[i]) > 1; i++)
		lglpushstk(lgl, lgl->gauss->occs + var, eqn);
}

static void
lglgaussdiseqn(LGL* lgl, int eqn)
{
	int *xors = lgl->gauss->xors.start, i, var;
	for (i = eqn; (var = xors[i]) > 1; i++) {
		xors[i] = NOTALIT;
		lgl->gauss->garbage++;
		INCSTEPS(gauss.steps.elim);
		lglrmstk(lgl->gauss->occs + var, eqn);
	}
	xors[i] = NOTALIT;
	lgl->gauss->garbage++;
}

static void
lglgaussconnect(LGL* lgl)
{
	int c, i, eox = lglcntstk(&lgl->gauss->xors), connected, var, vars;
	const int* xors = lgl->gauss->xors.start;
	Stk* occs;
	LOG(2, "connecting equations");
	assert(!lgl->gauss->occs);
	NEW(lgl->gauss->occs, lgl->nvars);
	vars = connected = 0;
	for (c = 0; c < eox; c = i + 1) {
		INCSTEPS(gauss.steps.elim);
		for (i = c; (var = xors[i]) > 1; i++) {
			occs = lgl->gauss->occs + var;
			if (lglmtstk(occs))
				vars++;
			lglpushstk(lgl, lgl->gauss->occs + var, c);
			connected++;
		}
	}
	lglprt(lgl,
		   1,
		   "[gauss-%d] connected %d occurrences of %d variables (average %.1f)",
		   lgl->stats->gauss.count,
		   connected,
		   vars,
		   lglavg(connected, vars));
}

static int
lglgaussorderidx(LGL* lgl, int var)
{
	assert(2 <= var && var < lgl->nvars);
	if (!lglmtstk(lgl->gauss->occs + var))
		lglpushstk(lgl, &lgl->gauss->order, var);
	return 1;
}

static void
lglgaussorder(LGL* lgl)
{
	lglrandidxtrav(lgl, lglgaussorderidx);
	NEW(lgl->gauss->eliminated, lgl->nvars);
}

static void
lglgaussdisconnect(LGL* lgl)
{
	int idx;
	assert(lgl->gauss->occs);
	LOG(2, "disconnecting equations");
	for (idx = 2; idx < lgl->nvars; idx++)
		lglrelstk(lgl, lgl->gauss->occs + idx);
	DEL(lgl->gauss->occs, lgl->nvars);
	assert(!lgl->gauss->occs);
}

static void
lglgaussextract(LGL* lgl)
{
	int extracted, lits;
	assert(lglsmallirr(lgl));
	if (lgl->level)
		lglbacktrack(lgl, 0);
	lglgc(lgl);
	if (lgl->mt)
		return;
	lgldense(lgl, 0);
	extracted = lglgaussextractsmall(lgl);
	extracted += lglgaussextractlarge(lgl);
	lits = lglcntstk(&lgl->gauss->xors) - extracted;
	lglprt(lgl,
		   1,
		   "[gauss-%d] extracted %d xors of average arity %.1f",
		   lgl->stats->gauss.count,
		   extracted,
		   lglavg(lits, extracted));
	lglsparse(lgl);
	lglgc(lgl);
	if (lgl->mt)
		return;
	lglfitstk(lgl, &lgl->gauss->xors);
}

static int
lglgaussoccs(LGL* lgl, int a)
{
	return lglcntstk(lgl->gauss->occs + a);
}

static int
lglcmpgauss(LGL* lgl, int a, int b)
{
	int res = lglgaussoccs(lgl, a) - lglgaussoccs(lgl, b);
	if (!res)
		res = a - b;
	return res;
}

#define LGLCMPGAUSS(A, B) lglcmpgauss(lgl, *(A), *(B))

static void
lglgaussort(LGL* lgl)
{
	int max = lglcntstk(&lgl->gauss->order), rest, *start;
	assert(lgl->gauss->next <= max);
	rest = max - lgl->gauss->next;
	start = lgl->gauss->order.start + lgl->gauss->next;
	ADDSTEPS(gauss.steps.elim, rest);
	SORT(int, start, rest, LGLCMPGAUSS);
	lglprt(lgl, 3, "[gauss-%d] sorted %d remaining variables", lgl->stats->gauss.count, rest);
}

static int
lglgausspickeqn(LGL* lgl, int pivot)
{
	int res, cand, weight, size, tmp, other, found;
	const int *p, *e, *q;
	Stk* occs;
	assert(lglgaussoccs(lgl, pivot));
	res = -1;
	weight = INT_MAX;
	size = INT_MAX;
	occs = lgl->gauss->occs + pivot;
	for (p = occs->start; p < occs->top; p++) {
		e = lgl->gauss->xors.start + (cand = *p);
		found = tmp = 0;
		INCSTEPS(gauss.steps.elim);
		for (q = e; (other = *q) > 1; q++) {
			if (lgl->gauss->eliminated[other])
				break;
			if (other == pivot) {
				assert(!found);
				found++;
				continue;
			}
			tmp += lglgaussoccs(lgl, other) - 1;
		}
		if (other > 1)
			continue;
		assert(found);
		if (res >= 0 && q - e >= size)
			continue;
		if (res >= 0 && q - e == size && tmp >= weight)
			continue;
		weight = tmp;
		size = q - e;
		res = cand;
	}
	if (res >= 0)
		LOGEQN(2, res, "picking size %d weight %d equation", size, weight);
	else
		LOG(2, "no uneliminated equation for pivot %d left", pivot);
	return res;
}

static void
lglcpystk(LGL* lgl, Stk* dst, Stk* src)
{
	const int* p;
	for (p = src->start; p < src->top; p++)
		lglpushstk(lgl, dst, *p);
}

static int
lglgaussaddeqn(LGL* lgl, int eqn)
{
	const int* p;
	AVar* av;
	int var;
	for (p = lgl->gauss->xors.start + eqn; (var = *p) > 1; p++) {
		av = lglavar(lgl, var);
		if (!av->mark)
			lglpushstk(lgl, &lgl->clause, var);
		av->mark = !av->mark;
	}
	return var;
}

static void
lglgaussubst(LGL* lgl, int pivot, int subst)
{
	Stk* occs = lgl->gauss->occs + pivot;
	int eqn, rhs, res;
	const int* p;
	int* q;
	assert(lglcntstk(occs) > 1);
	while (lglcntstk(occs) > 1) {
		if (lglterminate(lgl))
			return;
		eqn = occs->start[0];
		if (eqn == subst)
			eqn = occs->start[1];
		assert(lglmtstk(&lgl->clause));
		LOGEQN(2, subst, "  1st row (kept)     ");
		rhs = lglgaussaddeqn(lgl, eqn);
		LOGEQN(2, eqn, "  2nd row (replaced) ");
		if (lglgaussaddeqn(lgl, subst))
			rhs = !rhs;
		lglgaussdiseqn(lgl, eqn);
		q = lgl->clause.start;
		for (p = q; p < lgl->clause.top; p++)
			if (lglmarked(lgl, *p))
				*q++ = *p;
		lgl->clause.top = q;
		if (!lglmtstk(&lgl->clause)) {
			res = lglcntstk(&lgl->gauss->xors);
			lglcpystk(lgl, &lgl->gauss->xors, &lgl->clause);
			lglpushstk(lgl, &lgl->gauss->xors, rhs);
			LOGEQN(2, res, "  result row         ");
			lglgaussconeqn(lgl, res);
		} else if (rhs == 0) {
			LOG(2, "trivial result row 0 = 0");
		} else {
			assert(rhs == 1);
			LOG(1, "inconsistent result row 0 = 1 from gaussian elimination");
			lgl->mt = 1;
		}
		lglpopnunmarkstk(lgl, &lgl->clause);
	}
}

static void
lglgausschkeliminated(LGL* lgl)
{
#ifndef NDEBUG
	int idx, eliminated, occs;
	if (!lgl->opts->check.val)
		return;
	for (idx = 2; idx < lgl->nvars; idx++) {
		eliminated = lgl->gauss->eliminated[idx];
		occs = lglgaussoccs(lgl, idx);
		if (eliminated == 1)
			assert(occs == 1);
		if (eliminated == 2)
			assert(occs == 0);
	}
#endif
	(void)lgl;
}

static void
lglgaussgc(LGL* lgl)
{
	int count = lglcntstk(&lgl->gauss->xors), *q;
	const int* p;
	if (lgl->gauss->garbage < count / 2 + 10000)
		return;
	lgl->stats->gauss.gcs++;
	lglprt(lgl, 2, "[gauss-%d] collecting %d garbage out of %d", lgl->stats->gauss.count, lgl->gauss->garbage, count);
	lglgaussdisconnect(lgl);
	q = lgl->gauss->xors.start;
	for (p = q; p < lgl->gauss->xors.top; p++)
		if (*p != NOTALIT)
			*q++ = *p;
	lgl->gauss->xors.top = q;
	lglfitstk(lgl, &lgl->gauss->xors);
	lglgaussconnect(lgl);
	lgl->gauss->garbage = 0;
}

static int
lglgausselimvar(LGL* lgl, int pivot)
{
	int subst, changed, occs, eliminated;
	LOG(2, "eliminating variable %d occurring %d times", pivot, lglgaussoccs(lgl, pivot));
	assert(!lgl->gauss->eliminated[pivot]);
	if (!(occs = lglgaussoccs(lgl, pivot))) {
		LOG(2, "variable %d disappeared ", pivot);
		eliminated = 2;
		changed = 0;
	} else if (occs == 1) {
		LOG(2, "eliminated variable %d occurrs exactly once", pivot);
		eliminated = 1;
		changed = 0;
	} else {
		lglgaussgc(lgl);
		subst = lglgausspickeqn(lgl, pivot);
		if (subst >= 0) {
			lglgaussubst(lgl, pivot, subst);
			eliminated = 1;
			changed = 1;
		} else {
			eliminated = 3;
			changed = 0;
		}
	}
	lgl->gauss->eliminated[pivot] = eliminated;
	lglgausschkeliminated(lgl);
	return changed;
}

static void
lglgausselim(LGL* lgl)
{
	int pivot, changed = 1;
	while (!lgl->mt && lgl->gauss->next < (int)lglcntstk(&lgl->gauss->order)) {
		if (lgl->stats->gauss.steps.elim >= lgl->limits->gauss.steps.elim)
			break;
		if (lglterminate(lgl))
			break;
		if (changed)
			lglgaussort(lgl);
		pivot = lglpeek(&lgl->gauss->order, lgl->gauss->next++);
		changed = lglgausselimvar(lgl, pivot);
	}
}

static void
lglgaussinit(LGL* lgl)
{
	assert(!lgl->gauss);
	NEW(lgl->gauss, 1);
}

static void
lglgaussreset(LGL* lgl)
{
	assert(lgl->gauss);
	if (lgl->gauss->occs)
		lglgaussdisconnect(lgl);
	if (lgl->gauss->eliminated)
		DEL(lgl->gauss->eliminated, lgl->nvars);
	lglrelstk(lgl, &lgl->gauss->xors);
	lglrelstk(lgl, &lgl->gauss->order);
	DEL(lgl->gauss, 1);
}

static int
lglgaussexp2(LGL* lgl, int a, int b)
{
	assert(lgl->gaussing);
	assert(lglmtstk(&lgl->clause));
	if (lglhasbin(lgl, a, b))
		return 0;
	lglpushstk(lgl, &lgl->clause, a);
	lglpushstk(lgl, &lgl->clause, b);
	lglpushstk(lgl, &lgl->clause, 0);
	LOGCLS(2, lgl->clause.start, "gauss exported binary clause");
	lgladdcls(lgl, REDCS, 0, 0);
	lglclnstk(&lgl->clause);
	return 1;
}

static int
lglgaussexp3(LGL* lgl, int a, int b, int c)
{
	assert(lgl->gaussing);
	assert(lglmtstk(&lgl->clause));
	if (lglhastrn(lgl, a, b, c))
		return 0;
	lglpushstk(lgl, &lgl->clause, a);
	lglpushstk(lgl, &lgl->clause, b);
	lglpushstk(lgl, &lgl->clause, c);
	lglpushstk(lgl, &lgl->clause, 0);
	LOGCLS(2, lgl->clause.start, "gauss exported ternary clause");
	lgladdcls(lgl, REDCS, 0, 0);
	lglclnstk(&lgl->clause);
	return 1;
}

static int
lglgaussexport(LGL* lgl)
{
	int var, size, val, rhs, unit, a, b, c, exported;
	const int *e, *p, *q;
	for (e = lgl->gauss->xors.start; e < lgl->gauss->xors.top; e = p + 1) {
		if (*(p = e) >= NOTALIT)
			continue;
		while ((var = *p) > 1)
			p++;
		rhs = *p;
		assert(lglmtstk(&lgl->clause));
		for (q = e; q < p; q++) {
			var = *q;
			val = lglval(lgl, var);
			if (val < 0)
				continue;
			if (val > 0) {
				rhs = !rhs;
				continue;
			}
			lglpushstk(lgl, &lgl->clause, var);
		}
		size = lglcntstk(&lgl->clause);
		if (!size && !rhs)
			continue;
		if (!size && rhs) {
			LOGEQN(1, e - lgl->gauss->xors.start, "gauss exporting inconsistent equation");
			return 0;
		}
		a = (size > 0) ? lgl->clause.start[0] : 0;
		b = (size > 1) ? lgl->clause.start[1] : 0;
		c = (size > 2) ? lgl->clause.start[2] : 0;
		lglclnstk(&lgl->clause);
		if (size == 1) {
			unit = a;
			if (!rhs)
				unit = -unit;
			LOG(1, "gauss exporting unit %d", unit);
			lgl->stats->gauss.units++;
			lglunit(lgl, unit);
		} else if (size == 2) {
			if (rhs)
				b = -b;
			exported = lglgaussexp2(lgl, -a, b);
			exported |= lglgaussexp2(lgl, a, -b);
			if (exported) {
				LOG(1, "gauss exporting equivalence %d = %d", a, b);
				lgl->stats->gauss.equivs++;
			}
		} else if (size == 3 && lgl->opts->gaussexptrn.val) {
			if (!rhs)
				c = -c;
			exported = lglgaussexp3(lgl, a, b, c);
			exported |= lglgaussexp3(lgl, a, -b, -c);
			exported |= lglgaussexp3(lgl, -a, b, -c);
			exported |= lglgaussexp3(lgl, -a, -b, c);
			if (exported) {
				LOG(1, "gauss exporting ternary equation %d + %d + %d = 1", a, b, c);
				lgl->stats->gauss.trneqs++;
			}
		}
	}
	return 1;
}

static void
lglsetgausslim(LGL* lgl)
{
	int64_t limit, irrlim;
	int pen, szpen;
	limit = (lgl->opts->gaussreleff.val * lglvisearch(lgl)) / 1000;
	if (limit < lgl->opts->gaussmineff.val)
		limit = lgl->opts->gaussmineff.val;
	if (lgl->opts->gaussmaxeff.val >= 0 && limit > lgl->opts->gaussmaxeff.val)
		limit = lgl->opts->gaussmaxeff.val;
	limit >>= (pen = lgl->limits->gauss.pen + (szpen = lglszpen(lgl)));
	irrlim = (lgl->stats->irr.clauses.cur / 2) >> szpen;
	if (lgl->opts->irrlim.val && limit < irrlim) {
		limit = irrlim;
		lglprt(lgl,
			   1,
			   "[gauss-%d] limit %lld based on %d irredundant clauses penalty %d",
			   lgl->stats->gauss.count,
			   (LGLL)limit,
			   lgl->stats->irr.clauses.cur,
			   szpen);
	} else
		lglprt(lgl,
			   1,
			   "[gauss-%d] limit %lld penalty %d = %d + %d",
			   lgl->stats->gauss.count,
			   (LGLL)limit,
			   pen,
			   lgl->limits->gauss.pen,
			   szpen);
	lgl->limits->gauss.steps.extr = lgl->stats->gauss.steps.extr + limit;
	lgl->limits->gauss.steps.elim = lgl->stats->gauss.steps.elim + limit;
}

static int
lglgauss(LGL* lgl)
{
	int oldunits, oldequivs, oldtrneqs;
	int units, equivs, trneqs;
	int success;
	assert(lgl->opts->gauss.val);
	if (lgl->mt)
		return 0;
	if (lgl->nvars <= 2)
		return 1;
	lglstart(lgl, &lgl->times->gauss);
	assert(!lgl->simp && !lgl->gaussing);
	lgl->simp = lgl->gaussing = 1;
	lgl->stats->gauss.count++;
	lgl->stats->gauss.extracted.last = 0;
	lgl->stats->gauss.arity.max.last = 0;
	lglsetgausslim(lgl);
	lglgaussinit(lgl);
	lglgaussextract(lgl);
	oldunits = lgl->stats->gauss.units;
	oldequivs = lgl->stats->gauss.equivs;
	oldtrneqs = lgl->stats->gauss.trneqs;
	if (!lglmtstk(&lgl->gauss->xors)) {
		lglgaussconnect(lgl);
		lglgaussorder(lgl);
		lglsetgausslim(lgl);
		lglgausselim(lgl);
		if (!lgl->mt && !lglterminate(lgl)) {
			if (lgl->opts->verbose.val >= 3)
				lglgdump(lgl);
			lglgaussdisconnect(lgl);
			if (!lglgaussexport(lgl) || !lglbcp(lgl))
				lglmt(lgl);
			else if (lgl->limits->gauss.steps.extr > lgl->stats->gauss.steps.extr &&
					 lgl->limits->gauss.steps.elim > lgl->stats->gauss.steps.elim)
				lglprt(lgl, 1, "[gauss-%d] fully completed", lgl->stats->gauss.count);
		}
	}
	lglgaussreset(lgl);
	units = lgl->stats->gauss.units - oldunits;
	equivs = lgl->stats->gauss.equivs - oldequivs;
	trneqs = lgl->stats->gauss.trneqs - oldtrneqs;
	success = units || equivs;
	if (!lgl->mt && success && !lglterminate(lgl) && lgl->opts->decompose.val)
		lgldecomp(lgl);
	if (trneqs)
		success = 1;
	if (lgl->mt)
		lglprt(lgl, 1, "[gauss-%d] proved unsatisfiability", lgl->stats->gauss.count);
	else {
		lglprt(lgl,
			   1,
			   "[gauss-%d] exported %d unary, %d binary and %d ternary equations",
			   lgl->stats->gauss.count,
			   units,
			   equivs,
			   trneqs);
	}
	LGLUPDPEN(gauss, success);
	lglrep(lgl, 2, 'G');
	assert(lgl->simp && lgl->gaussing);
	lgl->simp = lgl->gaussing = 0;
	lglstop(lgl);
	return !lgl->mt;
}

/*------------------------------------------------------------------------*/

#define CARDLOGLEVEL 2

static int
lglcardsub(LGL* lgl, const int* lits, int bound)
{
	int count, size, res, minlit, minocc, nocc, lit, pos, otherbound;
	Card* card = lgl->card;
	const int *p, *c, *q;
	Stk* s;
	minocc = INT_MAX;
	minlit = 0;
	for (p = lits; minocc && (lit = *p); p++) {
		lglmarkunmarked(lgl, lit);
		s = card->occs + lit;
		INCSTEPS(card.steps);
		nocc = lglcntstk(s);
		if (nocc > minocc)
			continue;
		minlit = lit;
		minocc = nocc;
	}
	res = 0;
	if (minocc && minlit) {
		size = p - lits;
		s = card->occs + minlit;
		for (p = s->start; p < s->top; p++) {
			pos = *p;
			c = card->cards.start + pos;
			INCSTEPS(card.steps);
			otherbound = *c;
			if (otherbound > bound)
				continue;
			count = 0;
			INCSTEPS(card.steps);
			for (q = c + 1; *q; q++)
				count++;
			if (count < size)
				continue;
			count = 0;
			INCSTEPS(card.steps);
			for (q = c + 1; (count < size) && (lit = *q); q++)
				if (lglmarked(lgl, lit) > 0)
					count++;
			if (count >= size) {
				LOGCLS(CARDLOGLEVEL, lits, "subsumed cardinality constraint %d >=", bound);
				LOGCLS(CARDLOGLEVEL, c + 1, "subsuming cardinality constraint %d >=", otherbound);
				res = 1;
			}
		}
	}
	for (p = lits; (lit = *p); p++)
		lglunmark(lgl, lit);
	return res;
}

static int
lglcard1sub(LGL* lgl, const int* lits)
{
	int res, minlit, minocc, nocc, lit, count, size;
	Card* card = lgl->card;
	const int *p, *c, *q;
	Stk* s;
	minocc = INT_MAX;
	minlit = 0;
	for (p = lits; minocc && (lit = *p); p++) {
		lglmarkunmarked(lgl, lit);
		s = card->occs + lit;
		INCSTEPS(card.steps);
		nocc = lglcntstk(s);
		if (nocc > minocc)
			continue;
		minlit = lit;
		minocc = nocc;
	}
	res = 0;
	if (!minocc || !minlit)
		goto DONE;
	size = p - lits;
	s = card->occs + minlit;
	count = 0;
	for (p = s->start; !res && p < s->top; p++) {
		c = card->atmost1.start + *p;
		for (q = c; (lit = *q); q++)
			if (lglmarked(lgl, lit) && ++count >= size)
				break;
		if (!lit)
			continue;
		LOGCLS(CARDLOGLEVEL, lits, "subsumed at-most-one cardinality constraint 1 >=");
		LOGCLS(CARDLOGLEVEL, c, "subsuming at-most-one cardinality constraint 1 >=");
		res = 1;
	}
DONE:
	for (p = lits; (lit = *p); p++)
		lglunmark(lgl, lit);
	return res;
}

static int
lglcard2sub(LGL* lgl, const int* lits, int* minlitptr)
{
	int res, minlit, minocc, nocc, lit, count, size;
	Card* card = lgl->card;
	const int *p, *c, *q;
	Stk* s;
	minocc = INT_MAX;
	minlit = 0;
	for (p = lits; minocc && (lit = *p); p++) {
		lglmarkunmarked(lgl, lit);
		s = card->occs + lit;
		INCSTEPS(card.steps);
		nocc = lglcntstk(s);
		if (nocc > minocc)
			continue;
		minlit = lit;
		minocc = nocc;
	}
	assert(!minocc || p + 1 == card->atmost2.top);
	res = 0;
	if (!minocc || !minlit || *minlitptr == minlit)
		goto DONE;
	*minlitptr = minlit;
	size = p - lits;
	s = card->occs + minlit;
	count = 0;
	for (p = s->start; !res && p < s->top; p++) {
		c = card->atmost2.start + *p;
		for (q = c; (lit = *q); q++)
			if (lglmarked(lgl, lit) && ++count >= size)
				break;
		if (!lit)
			continue;
		LOGCLS(CARDLOGLEVEL, lits, "subsumed at-most-two cardinality constraint 2 >=");
		LOGCLS(CARDLOGLEVEL, c, "subsuming at-most-two cardinality constraint 2 >=");
		res = 1;
	}
DONE:
	for (p = lits; (lit = *p); p++)
		lglunmark(lgl, lit);
	return res;
}

static int
lgladdcard(LGL* lgl, const int* lits, int bound, int* subsumed_ptr)
{
	Card* card = lgl->card;
	const int* p;
	int start, lit;
#if !defined(NDEBUG) || !defined(NLGLOG)
	int size = 0;
	for (p = lits; *p; p++)
		size++;
#endif
	if (lglcardsub(lgl, lits, bound)) {
		lgl->stats->card.subsumed++;
		if (subsumed_ptr)
			*subsumed_ptr += 1;
		return 0;
	}
	LOGCLS(CARDLOGLEVEL, lits, "new %s cardinality constraint %d >=", (bound + 1 < size ? "real" : "pseudo"), bound);
	assert(bound <= size);
	assert(size <= lglfactor(lgl, lgl->stats->card.count, lgl->opts->cardmaxlen.val));
#ifndef NDEBUG
	for (p = lits; (lit = *p); p++)
		assert(!card->eliminated[abs(lit)]);
	for (p = lits; (lit = *p); p++)
		assert(!card->count[abs(lit)]);
#endif
	assert(card);
	start = lglcntstk(&card->cards);
	lglpushstk(lgl, &card->cards, bound);
	for (p = lits; (lit = *p); p++) {
		lglpushstk(lgl, &card->cards, lit);
		lglpushstk(lgl, card->occs + lit, start);
	}
	lglpushstk(lgl, &card->cards, 0);
	return 1;
}

static void
lglcardfmstep(LGL* lgl, int pivot, int cardposidx, int cardnegidx)
{
	int bn, bp, ln, lp, b, c, s, lit, idx, addcard, len, unit[2], div, elim;
	const int cardcut = lgl->opts->cardcut.val;
	const int *p, *q, *cn, *cp;
	int *r, divsame, cardmaxlen;
	Card* card = lgl->card;
	INCSTEPS(card.steps);
	lgl->stats->card.resolved++;
	assert(0 <= cardposidx && cardposidx < (int)lglcntstk(&card->cards));
	assert(0 <= cardnegidx && cardnegidx < (int)lglcntstk(&card->cards));
	cp = card->cards.start + cardposidx;
	cn = card->cards.start + cardnegidx;
	bp = *cp++;
	assert(bp >= 0);
	bn = *cn++;
	assert(bp >= 0);
	LOGCLS(CARDLOGLEVEL, cp, "first antecedent fourier-motzkin on %d cardinality constraint %d >=", pivot, bp);
	LOGCLS(CARDLOGLEVEL, cn, "second antecedent fourier-motzkin on %d cardinality constraint %d >=", -pivot, bn);
	b = bp + bn;
	LOG(CARDLOGLEVEL, "starting with bound %d", b);
	assert(lglmtstk(&lgl->clause));
	for (p = cp; (lit = *p); p++) {
		idx = abs(lit);
		assert(!card->count[idx]);
		lglpushstk(lgl, &lgl->clause, idx);
		card->count[idx] += lglsgn(lit);
	}
	lp = p - cp;
	assert(bp < lp);
	elim = 0;
	div = 1;
	for (q = cn; (lit = *q); q++) {
		idx = abs(lit);
		c = card->count[idx];
		assert(!c || abs(c) == 1);
		s = lglsgn(lit);
		if (!c)
			lglpushstk(lgl, &lgl->clause, idx);
		card->count[idx] += s;
		if (c && c != s)
			elim++;
		if (c == s)
			div = 2;
	}
	ln = q - cn;
	assert(bn < ln);
	divsame = 1;
	for (p = lgl->clause.start; divsame && p < lgl->clause.top; p++) {
		idx = abs(*p);
		c = card->count[idx];
		if (!c)
			continue;
		if (c < 0)
			c = -c;
		divsame = (c == div);
	}
#ifndef NLGLOG
	if (lgl->opts->log.val >= CARDLOGLEVEL) {
		int first = 1;
		lglogstart(lgl, CARDLOGLEVEL, "pseudo-boolean resolvent %d - %d >= ", b, elim);
		for (p = lgl->clause.start; p < lgl->clause.top; p++) {
			idx = *p;
			assert(1 < idx), assert(idx < lgl->nvars);
			c = card->count[idx];
			if (!c)
				continue;
			if (!first)
				fprintf(lgl->out, " %c ", (c < 0) ? '-' : '+');
			else if (c < 0)
				fputs("-", lgl->out);
			first = 0;
			c = abs(c);
			assert(c > 0);
			if (c != 1)
				fprintf(lgl->out, "%d*", c);
			fprintf(lgl->out, "%d", idx);
		}
		if (first)
			fputs("0", lgl->out);
		lglogend(lgl);
	}
#endif
	LOG(CARDLOGLEVEL,
		"size %d cardinalty resolvent contains %d eliminated division %d %s",
		lglcntstk(&lgl->clause) - elim,
		elim,
		div,
		divsame ? "all" : "some");
	r = lgl->clause.start;
	for (p = r; p < lgl->clause.top; p++) {
		idx = *p;
		c = card->count[idx];
		if (divsame || cardcut == 1)
			c /= div;
		else if (abs(c) != 1 && cardcut == 2 && div == 2)
			c = 0;
		if (!c)
			continue;
		lit = (c < 0) ? -idx : idx;
		*r++ = lit;
	}
	lgl->clause.top = r;
	b -= elim;
	assert(div == 1 || div == 2);
	if ((divsame || cardcut == 1) && div == 2)
		b /= 2;
	len = lglcntstk(&lgl->clause);
	lglpushstk(lgl, &lgl->clause, 0);
	addcard = 0;
	cardmaxlen = lglfactor(lgl, lgl->stats->card.count, lgl->opts->cardmaxlen.val);
	if (!cardcut && div > 1) {
		LOG(CARDLOGLEVEL, "ignoring resolved cardinality constraint which requires cut");
		assert(!addcard);
	} else if (b < 0) {
		assert(!addcard);
		assert(!lgl->mt);
		LOGCLS(CARDLOGLEVEL, lgl->clause.start, "inconsistent cardinality constraint %d >=", b);
		lglmt(lgl);
	} else if (b == 0) {
		assert(!addcard);
		LOGCLS(CARDLOGLEVEL, lgl->clause.start, "forcing cardinality constraint %d >=", b);
		for (p = lgl->clause.start; p < lgl->clause.top - 1; p++) {
			lit = *p;
			assert(lit);
			lgl->stats->card.units++;
			lglpushstk(lgl, &lgl->card->units, -lit);
			card->count[abs(lit)] = 0;
			unit[0] = lit;
			unit[1] = 0;
			(void)lgladdcard(lgl, unit, 0, 0);
		}
	} else if (b >= len) {
		LOGCLS(CARDLOGLEVEL, lgl->clause.start, "trivial resolved cardinality constraint %d >=", b);
		assert(!addcard);
	} else if (len > cardmaxlen) {
		LOGCLS(CARDLOGLEVEL,
			   lgl->clause.start,
			   "length %d exceeds limit %d of resolved cardinality constraint %d >=",
			   len,
			   cardmaxlen,
			   b);
	} else {
		addcard = 1;
		assert(0 < b);
		LOGCLS(CARDLOGLEVEL, lgl->clause.start, "resolved cardinality constraint %d >=", b);
		if (b == 1 && ((ln >= 3 && lp >= 3) || (int64_t)lglcntstk(&lgl->clause) >
												   lglfactor(lgl, lgl->stats->card.count, lgl->opts->cardexpam1.val))) {
			LOGCLS(CARDLOGLEVEL, lgl->clause.start, "saving to export at-most-one constraint 1 >=");
			for (p = lgl->clause.start; p < lgl->clause.top; p++)
				lglpushstk(lgl, &lgl->card->expam1, *p);
		}
	}
	cp = card->cards.start + cardposidx + 1;
	cn = card->cards.start + cardnegidx + 1;
	for (p = cp; (lit = *p); p++)
		card->count[abs(lit)] = 0;
	for (q = cn; (lit = *q); q++)
		card->count[abs(lit)] = 0;
	if (addcard > 0)
		(void)lgladdcard(lgl, lgl->clause.start, b, 0);
	lglclnstk(&lgl->clause);
	// COVER (cardcut == 2 && div == 2 && divsame);
	(void)pivot;
}

static void
lglrmcardexcept(LGL* lgl, int cardidx, int except)
{
	Card* card = lgl->card;
	int *c, *p, lit;
	assert(0 <= cardidx && cardidx < (int)lglcntstk(&card->cards));
	c = card->cards.start + cardidx + 1;
	LOGCLS(CARDLOGLEVEL, c, "removing cardinality constraint %d >=", c[-1]);
	for (p = c; (lit = *p); p++)
		if (lit != except)
			lglrmstk(card->occs + lit, cardidx);
}

static int
lglcardocc(LGL* lgl, int a)
{
	return lglcntstk(lgl->card->occs + a);
}

static int
lglcmpcard(LGL* lgl, int a, int b)
{
	int64_t s = lglcardocc(lgl, a) * (int64_t)lglcardocc(lgl, -a);
	int64_t t = lglcardocc(lgl, b) * (int64_t)lglcardocc(lgl, -b);
	if (s > t)
		return -1;
	if (s < t)
		return 1;
	return b - a;
}

#define LGLCMPCARD(A, B) lglcmpcard(lgl, *(A), *(B))

static void
lglcardresched(LGL* lgl)
{
	Stk* elim = &lgl->card->elim;
	int count = lglcntstk(elim);
	ADDSTEPS(card.steps, 4 * count);
	SORT(int, elim->start, lglcntstk(elim), LGLCMPCARD);
	lgl->stats->card.resched++;
}

static void
lglcardfmlit(LGL* lgl, int pivot)
{
	Stk *poccs = lgl->card->occs + pivot, *noccs = lgl->card->occs - pivot;
	int pcnt = lglcntstk(poccs), ncnt = lglcntstk(noccs);
	int count = lgl->stats->card.count;
	const int *p, *q;
	assert(pivot > 0);
	assert(!lgl->card->eliminated[pivot]);
	if (!pcnt || !ncnt)
		goto DONE;
	if (pcnt > lglfactor(lgl, lgl->opts->cardocclim1.val, count))
		goto DONE;
	if (ncnt > lglfactor(lgl, lgl->opts->cardocclim1.val, count))
		goto DONE;
	if (pcnt > lglfactor(lgl, lgl->opts->cardocclim2.val, count) &&
		ncnt > lglfactor(lgl, lgl->opts->cardocclim2.val, count))
		goto DONE;
	lgl->stats->card.eliminated++;
	lgl->card->eliminated[pivot] = 1;
	LOG(CARDLOGLEVEL, "fourier-motzkin pivot %d with %d positive and %d negative occurrences", pivot, pcnt, ncnt);
	for (p = poccs->start; !lgl->mt && p < poccs->top; p++)
		for (q = noccs->start; !lgl->mt && q < noccs->top; q++)
			lglcardfmstep(lgl, pivot, *p, *q);
DONE:
	for (p = poccs->start; !lgl->mt && p < poccs->top; p++) {
		lglrmcardexcept(lgl, *p, pivot);
		INCSTEPS(card.steps);
	}
	for (q = noccs->start; !lgl->mt && q < noccs->top; q++) {
		lglrmcardexcept(lgl, *q, -pivot);
		INCSTEPS(card.steps);
	}
	lglrelstk(lgl, poccs);
	lglrelstk(lgl, noccs);
	if (!(lgl->stats->card.eliminated % lgl->opts->cardreschedint.val))
		lglcardresched(lgl);
}

static void
lglcardfm(LGL* lgl)
{
	Stk* elim;
	lglcardresched(lgl);
	elim = &lgl->card->elim;
	while (!lgl->mt && !lglmtstk(elim) && !lglterminate(lgl) && lgl->limits->card.steps >= INCSTEPS(card.steps))
		lglcardfmlit(lgl, lglpopstk(elim));
}

static int64_t
lglsetcardlim(LGL* lgl)
{
	int64_t limit, irrlim;
	int pen, szpen;
	limit = (lgl->opts->cardreleff.val * lglvisearch(lgl)) / 1000;
	if (limit < lgl->opts->cardmineff.val)
		limit = lgl->opts->cardmineff.val;
	if (lgl->opts->cardmaxeff.val >= 0 && limit > lgl->opts->cardmaxeff.val)
		limit = lgl->opts->cardmaxeff.val;
	limit >>= (pen = lgl->limits->card.pen + (szpen = lglszpen(lgl)));
	irrlim = (lgl->stats->irr.clauses.cur / 4) >> szpen;
	if (lgl->opts->irrlim.val && limit < irrlim) {
		limit = irrlim;
		lglprt(lgl,
			   1,
			   "[card-%d] limit %lld based on %d irredundant clauses penalty %d",
			   lgl->stats->card.count,
			   (LGLL)limit,
			   lgl->stats->irr.clauses.cur,
			   szpen);
	} else
		lglprt(lgl,
			   1,
			   "[card-%d] limit %lld penalty %d = %d + %d",
			   lgl->stats->card.count,
			   (LGLL)limit,
			   pen,
			   lgl->limits->card.pen,
			   szpen);
	lgl->limits->card.steps = lgl->stats->card.steps + limit;
	return limit;
}

static void
lglsetcardlimagain(LGL* lgl, int64_t limit)
{
	lglprt(lgl,
		   1,
		   "[card-%d] using previous limit %lld for elimination",
		   lgl->stats->card.count,
		   (LGLL)limit,
		   lgl->stats->irr.clauses.cur);
	lgl->limits->card.steps = lgl->stats->card.steps + limit;
}

static int
lglcard1extractlit(LGL* lgl, int lit)
{
	const int ignused = lgl->opts->cardignused.val;
	int blit, tag, other, other2, i, j, cntother;
	int start, size, trivial, subsumed;
	const int *p, *w, *eow;
	Card* card;
	HTS* hts;
	lgl->stats->card.found.am1.last.cnt = 0;
	lgl->stats->card.found.am1.last.max = 0;
	if (lglterminate(lgl))
		return 0;
	if (lgl->limits->card.steps < INCSTEPS(card.steps))
		return 0;
	card = lgl->card;
	if (ignused && card->lit2used[lit])
		return 1;
	start = lglcntstk(&card->atmost1);
	LOG(CARDLOGLEVEL + 1, "starting at-most-one clique[%d] for %d", start, lit);
	lglpushstk(lgl, &card->atmost1, lit);
	card->marked[lit] = 1;
	hts = lglhts(lgl, -lit);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	INCSTEPS(card.steps);
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag != BINCS)
			continue;
		other = -(blit >> RMSHFT);
		if (ignused && card->lit2used[other])
			continue;
		for (i = start + 1; i < (int)lglcntstk(&card->atmost1); i++) {
			other2 = lglpeek(&card->atmost1, i);
			if (!lglhasbin(lgl, -other, -other2))
				break;
		}
		if (i < (int)lglcntstk(&card->atmost1))
			continue;
		card->marked[other] = 1;
		assert(i == (int)lglcntstk(&card->atmost1));
		lglpushstk(lgl, &card->atmost1, other);
		LOG(CARDLOGLEVEL + 1, "adding %d to at-most-one clique[%d] for %d", other, start, lit);
		cntother = lglhts(lgl, -other)->count;
		for (j = start + 1; j < i; j++) {
			int tmp = lglpeek(&card->atmost1, j);
			int cntmp = lglhts(lgl, -tmp)->count;
			if (cntmp > cntother)
				break;
		}
		if (j < i) {
			int k;
			for (k = i; k > j; k--)
				card->atmost1.start[k] = card->atmost1.start[k - 1];
			card->atmost1.start[j] = other;
		}
#ifndef NDEBUG
		for (j = start + 1; j + 1 < i; j++)
			assert(lglhts(lgl, -lglpeek(&card->atmost1, j))->count <=
				   lglhts(lgl, -lglpeek(&card->atmost1, j + 1))->count);
#endif
	}
	size = lglcntstk(&card->atmost1) - start;
	trivial = (size <= 2);
	if (!ignused) {
		lglpushstk(lgl, &card->atmost1, 0);
		subsumed = lglcard1sub(lgl, card->atmost1.start + start);
		lglpopstk(&card->atmost1);
	} else
		subsumed = 0;
	for (p = card->atmost1.start + start; p < card->atmost1.top; p++) {
		other = *p;
		assert(card->marked[other]);
		card->marked[other] = 0;
		if (!trivial && !subsumed)
			card->lit2used[other] = 1;
	}
	if (trivial || subsumed) {
		LOG(CARDLOGLEVEL + 1, "resetting at-most-one clique[%d] for %d of trivial size %d", start, lit, size);
		lglrststk(&card->atmost1, start);
	} else {
#ifndef NLGLOG
		if (lgl->opts->log.val >= CARDLOGLEVEL) {
			lglogstart(lgl, CARDLOGLEVEL, "non trivial size %d at-most-one constraint 1 >= ", size);
			for (i = start; i < start + size; i++) {
				if (i > start)
					fputs(" + ", lgl->out);
				fprintf(lgl->out, "%d", lglpeek(&card->atmost1, i));
			}
			lglogend(lgl);
		}
#endif
		if (!ignused) {
			for (i = start; i < start + size; i++) {
				other = lglpeek(&card->atmost1, i);
				lglpushstk(lgl, &card->occs[other], start);
			}
		}
		lglpushstk(lgl, &card->atmost1, 0);
		lgl->stats->card.found.am1.total.sum += size;
		lgl->stats->card.found.am1.total.cnt++;
		lgl->stats->card.found.am1.last.cnt++;
		if (lgl->stats->card.found.am1.total.max < size)
			lgl->stats->card.found.am1.total.max = size;
		if (lgl->stats->card.found.am1.last.max < size)
			lgl->stats->card.found.am1.last.max = size;
	}
	return 1;
}

static void
lglcardreloccs(LGL* lgl)
{
	Card* card = lgl->card;
	int sign, idx, lit;
	assert(card), assert(!lgl->nvars || card->occs);
	for (sign = -1; sign <= 1; sign += 2)
		for (idx = 2; idx < lgl->nvars; idx++) {
			lit = sign * idx;
			lglrelstk(lgl, &card->occs[lit]);
		}
	card->occs -= lgl->nvars;
	DEL(card->occs, 2 * lgl->nvars);
}

static int
lglcard1extract(LGL* lgl)
{
	struct
	{
		int cnt;
		int64_t sum;
	} before, after, delta;
	const int ignused = lgl->opts->cardignused.val;
	Card* card = lgl->card;

	lglpushstk(lgl, &card->atmost1, 0);

	NEW(card->lit2used, 2 * lgl->nvars);
	card->lit2used += lgl->nvars;
	NEW(card->marked, 2 * lgl->nvars);
	card->marked += lgl->nvars;
	if (!ignused) {
		NEW(card->occs, 2 * lgl->nvars);
		card->occs += lgl->nvars;
	}

	before.cnt = lgl->stats->card.found.am1.total.cnt;
	before.sum = lgl->stats->card.found.am1.total.sum;

	lglrandlitrav(lgl, lglcard1extractlit);

	after.cnt = lgl->stats->card.found.am1.total.cnt;
	after.sum = lgl->stats->card.found.am1.total.sum;
	assert(after.cnt >= before.cnt);
	assert(after.sum >= before.sum);
	delta.cnt = after.cnt - before.cnt;
	delta.sum = after.sum - before.sum;

	card->lit2used -= lgl->nvars;
	DEL(card->lit2used, 2 * lgl->nvars);
	card->marked -= lgl->nvars;
	DEL(card->marked, 2 * lgl->nvars);
	if (!ignused)
		lglcardreloccs(lgl);

	if (delta.cnt)
		lglprt(lgl,
			   1,
			   "[card-%d] found %d at-most-one constraints of average size %.1f",
			   lgl->stats->card.count,
			   delta.cnt,
			   lglavg(delta.sum, delta.cnt));
	else
		lglprt(lgl, 1, "[card-%d] no at-most-one constraint found", lgl->stats->card.count);

	return delta.cnt;
}

static int
lglcard2extractlit(LGL* lgl, int lit)
{
	int blit, tag, other, other2, other3, i, j, k, res = 1;
	const int ignused = lgl->opts->cardignused.val;
	int start, size, minlit;
	const int *p, *w, *eow;
	int* q;
	Card* card;
	HTS* hts;
	lgl->stats->card.found.am2.last.cnt = 0;
	lgl->stats->card.found.am2.last.max = 0;
	if (lglterminate(lgl))
		return 0;
	if (lgl->limits->card.steps < INCSTEPS(card.steps))
		return 0;
	card = lgl->card;
	if (ignused && card->lit2used[lit])
		return 1;

	start = lglcntstk(&card->atmost2);
	LOG(CARDLOGLEVEL + 1, "starting at-most-two clique[%d] for %d", start, lit);
	assert(!card->lit2count[lit]);
	hts = lglhts(lgl, -lit);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	INCSTEPS(card.steps);
	for (p = w; p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag != TRNCS)
			continue;
		other = -(blit >> RMSHFT);
		other2 = -*p;
		if (card->lit2count[-other] || card->lit2count[-other2])
			continue;
		if (!card->lit2count[lit]++)
			lglpushstk(lgl, &card->atmost2, lit);
		if (!card->lit2count[other]++)
			lglpushstk(lgl, &card->atmost2, other);
		if (!card->lit2count[other2]++)
			lglpushstk(lgl, &card->atmost2, other2);
	}
	q = card->atmost2.start + start;
	for (p = q; p < card->atmost2.top; p++) {
		if (card->lit2count[other = *p] > 1)
			*q++ = other;
		else
			card->lit2count[other] = 0;
	}
	card->atmost2.top = q;
	size = lglcntstk(&card->atmost2) - start;
	if (size < 4) {
		LOG(CARDLOGLEVEL + 1, "less than 4 literals occuring 2 times in ternary clauses with %d", -lit);
	FAILED:
		while ((int)lglcntstk(&card->atmost2) > start) {
			int lit = lglpopstk(&card->atmost2);
			assert(card->lit2count[lit] > 0);
			card->lit2count[lit] = 0;
		}
		return res;
	}
	minlit = 0;
RESTART:
	if (!ignused) {
		int subsumed;
		lglpushstk(lgl, &card->atmost2, 0);
		subsumed = lglcard2sub(lgl, card->atmost2.start + start, &minlit);
		lglpopstk(&card->atmost2);
		if (subsumed)
			goto FAILED;
	}
	LOG(CARDLOGLEVEL + 1, "trying to connect %d literals", size - 1);
	for (i = 0; i < size - 2; i++) {
		other = -lglpeek(&card->atmost2, start + i);
		for (j = i + 1; j < size - 1; j++) {
			other2 = -lglpeek(&card->atmost2, start + j);
			for (k = j + 1; k < size; k++) {
				if (lgl->limits->card.steps < INCSTEPS(card.steps)) {
					LOG(CARDLOGLEVEL + 1, "cardinality extraction step limit hit");
					res = 0;
					goto FAILED;
				}
				other3 = -lglpeek(&card->atmost2, start + k);
				if (!lglhastrn(lgl, other, other2, other3)) {
					LOG(CARDLOGLEVEL + 1, "ternary clause %d %d %d missing", other, other2, other3);
					assert(size >= 4);
					if (size == 4)
						goto FAILED;
					int l, tmp;
					if (k > j + 1)
						l = k;
					else {
						int occ2 = card->lit2count[-other2];
						int occ3 = card->lit2count[-other3];
						if (occ2 > occ3)
							l = k;
						else if (occ2 < occ3)
							l = j;
						else {
							occ2 = lglhts(lgl, other2)->count;
							occ3 = lglhts(lgl, other3)->count;
							if (occ2 > occ3)
								l = k;
							else if (occ2 < occ3)
								l = j;
							else
								l = (lglrand(lgl) & 1) ? j : k;
						}
					}
					tmp = lglpeek(&card->atmost2, start + l);
					assert(abs(tmp) != abs(lit));
					assert(card->lit2count[tmp] > 0);
					card->lit2count[tmp] = 0;
					while (l + 1 < size) {
						tmp = lglpeek(&card->atmost2, start + l + 1);
						assert(abs(tmp) != abs(lit));
						lglpoke(&card->atmost2, start + l++, tmp);
					}
					assert(l == size - 1);
					card->atmost2.top--;
					size--;
					assert(size >= 4);
					assert(card->atmost2.start + start + size == card->atmost2.top);
					goto RESTART;
				}
			}
		}
	}
#ifndef NLGLOG
	if (lgl->opts->log.val >= CARDLOGLEVEL) {
		lglogstart(lgl, CARDLOGLEVEL, "non trivial size %d at-most-two constraint 2 >= ", size);
		for (i = start; i < start + size; i++) {
			if (i > start)
				fputs(" + ", lgl->out);
			fprintf(lgl->out, "%d", lglpeek(&card->atmost2, i));
		}
		lglogend(lgl);
	}
#endif
	for (i = start; i < start + size; i++) {
		other = lglpeek(&card->atmost2, i);
		card->lit2count[other] = 0;
		card->lit2used[other] = 1;
		if (!ignused)
			lglpushstk(lgl, &card->occs[other], start);
	}
	lglpushstk(lgl, &card->atmost2, 0);
	lgl->stats->card.found.am2.total.sum += size;
	lgl->stats->card.found.am2.total.cnt++;
	lgl->stats->card.found.am2.last.cnt++;
	if (lgl->stats->card.found.am2.total.max < size)
		lgl->stats->card.found.am2.total.max = size;
	if (lgl->stats->card.found.am2.last.max < size)
		lgl->stats->card.found.am2.last.max = size;
	return res;
}

static int
lglcard2extract(LGL* lgl)
{
	struct
	{
		int cnt;
		int64_t sum;
	} before, after, delta;
	const int ignused = lgl->opts->cardignused.val;
	Card* card = lgl->card;

	lglpushstk(lgl, &card->atmost2, 0);
	NEW(card->lit2used, 2 * lgl->nvars);
	card->lit2used += lgl->nvars;
	NEW(card->lit2count, 2 * lgl->nvars);
	card->lit2count += lgl->nvars;
	if (!ignused) {
		NEW(card->occs, 2 * lgl->nvars);
		card->occs += lgl->nvars;
	}

	before.cnt = lgl->stats->card.found.am2.total.cnt;
	before.sum = lgl->stats->card.found.am2.total.sum;

	lglrandlitrav(lgl, lglcard2extractlit);

	after.cnt = lgl->stats->card.found.am2.total.cnt;
	after.sum = lgl->stats->card.found.am2.total.sum;
	assert(after.cnt >= before.cnt);
	assert(after.sum >= before.sum);
	delta.cnt = after.cnt - before.cnt;
	delta.sum = after.sum - before.sum;

	card->lit2used -= lgl->nvars;
	DEL(card->lit2used, 2 * lgl->nvars);
	card->lit2count -= lgl->nvars;
	DEL(card->lit2count, 2 * lgl->nvars);
	if (!ignused)
		lglcardreloccs(lgl);

	if (delta.cnt)
		lglprt(lgl,
			   1,
			   "[card-%d] found %d at-most-two constraints of average size %.1f",
			   lgl->stats->card.count,
			   delta.cnt,
			   lglavg(delta.sum, delta.cnt));
	else
		lglprt(lgl, 1, "[card-%d] no at-most-two constraint found", lgl->stats->card.count);

	return delta.cnt;
}

static int
lglcarduseclswithlit(LGL* lgl, int lit)
{
	int pos = !lglmtstk(lgl->card->occs + lit);
	int neg = !lglmtstk(lgl->card->occs - lit);
	int level = lgl->opts->carduse.val;
	switch (level) {
		case 0:
			return 0;
		case 1:
			return pos && !neg;
		case 2:
			return pos;
		default:
			assert(level == 3);
			return pos || neg;
	}
}

static int
lglcardelim(LGL* lgl, int count)
{
	int blit, tag, other, other2, bound, used;
	int idx, sign, lit, start, len, res, glue;
	const int *p, *w, *eow, *c, *q;
	int cardmaxlen, subsumed = 0;
	Card* card = lgl->card;
	int clause[4];
	HTS* hts;
	Stk* s;

	NEW(card->occs, 2 * lgl->nvars);
	NEW(card->eliminated, lgl->nvars);
	NEW(card->count, lgl->nvars);
	card->occs += lgl->nvars;
	used = 0;
	cardmaxlen = lglfactor(lgl, lgl->stats->card.count, lgl->opts->cardmaxlen.val);
	for (start = 1; start < (int)lglcntstk(&card->atmost1); start++) {
		for (len = 0; lglpeek(&card->atmost1, start + len); len++)
			;
		if (len >= lgl->opts->cardminlen.val && len <= cardmaxlen &&
			lgladdcard(lgl, card->atmost1.start + start, 1, &subsumed)) {
			lgl->stats->card.used.am1.sum += len;
			lgl->stats->card.used.am1.cnt++;
			used++;
		}
		start += len;
	}
	for (start = 1; start < (int)lglcntstk(&card->atmost2); start++) {
		for (len = 0; lglpeek(&card->atmost2, start + len); len++)
			;
		if (len >= lgl->opts->cardminlen.val && len <= cardmaxlen &&
			lgladdcard(lgl, card->atmost2.start + start, 2, &subsumed)) {
			lgl->stats->card.used.am2.sum += len;
			lgl->stats->card.used.am2.cnt++;
			used++;
		}
		start += len;
	}
	if (used) {
		lglprt(lgl,
			   1,
			   "[card-%d] using %d out of %d constraints (%.0f%%), %d subsumed",
			   lgl->stats->card.count,
			   used,
			   count,
			   lglpcnt(used, count),
			   subsumed);
	} else {
		lglprt(lgl, 1, "[card-%d] no constraint out of %d used (%d subsumed)", lgl->stats->card.count, count, subsumed);
		goto SKIP;
	}
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (lglmtstk(card->occs + idx) && lglmtstk(card->occs - idx))
			continue;
		LOG(CARDLOGLEVEL, "variable %d scheduled for elimination", idx);
		lglpushstk(lgl, &card->elim, idx);
	}
	if (!lgl->opts->carduse.val)
		goto DO_NOT_USE_CLAUSES;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (lglterminate(lgl) || lgl->limits->card.steps < INCSTEPS(card.steps))
			goto SKIP;
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			if (!lglcarduseclswithlit(lgl, lit))
				continue;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (tag == BINCS) {
					other = blit >> RMSHFT;
					if (abs(other) < idx)
						continue;
					clause[0] = -lit, clause[1] = -other, clause[2] = 0;
					(void)lgladdcard(lgl, clause, 1, 0);
				} else if (tag == TRNCS) {
					other = blit >> RMSHFT;
					if (abs(other) < idx)
						continue;
					other2 = *p;
					if (abs(other2) < idx)
						continue;
					clause[0] = -lit, clause[1] = -other, clause[2] = -other2, clause[3] = 0;
					(void)lgladdcard(lgl, clause, 2, 0);
				} else
					assert(tag == LRGCS);
			}
		}
	}
	for (glue = -1; glue <= lgl->opts->cardglue.val; glue++) {
		s = (glue < 0) ? &lgl->irr : lgl->red + glue;
		for (c = s->start; c < s->top; c = p + 1) {
			if (*(p = c) >= NOTALIT)
				continue;
			for (p = c; *p; p++)
				;
			for (q = c; (lit = *q); q++)
				if (lglcarduseclswithlit(lgl, lit))
					break;
			if (!lit)
				continue;
			len = p - c;
			if (len > cardmaxlen)
				continue;
			assert(len >= 4);
			bound = len - 1;
			assert(lglmtstk(&lgl->clause));
			for (q = c; (lit = *q); q++)
				lglpushstk(lgl, &lgl->clause, -lit);
			lglpushstk(lgl, &lgl->clause, 0);
			(void)lgladdcard(lgl, lgl->clause.start, bound, 0);
			lglclnstk(&lgl->clause);
			if (lglterminate(lgl) || lgl->limits->card.steps < INCSTEPS(card.steps))
				goto SKIP;
		}
	}
DO_NOT_USE_CLAUSES:
	lglcardfm(lgl);
SKIP:
	lglcardreloccs(lgl);
	DEL(card->count, lgl->nvars);
	lglrelstk(lgl, &card->cards);
	lglrelstk(lgl, &card->elim);
	DEL(card->eliminated, lgl->nvars);
	if (lgl->mt)
		lglprt(lgl, 1, "[card-%d] produced empty clause", lgl->stats->card.count);
	else if (!lglmtstk(&card->units)) {
		lglprt(lgl, 1, "[card-%d] found %d units", lgl->stats->card.count, lglcntstk(&card->units));
		while (!lgl->mt && !lglmtstk(&card->units)) {
			lit = lglpopstk(&card->units);
			if (lglval(lgl, lit) > 0)
				continue;
			if (lglval(lgl, lit) < 0) {
				lglprt(lgl, 1, "[card-%d] found inconsistent unit", lgl->stats->card.count);
				lglmt(lgl);
			} else {
				lglunit(lgl, lit);
				if (!lglbcp(lgl)) {
					lglprt(lgl, 1, "[card-%d] inconsistent unit propagation", lgl->stats->card.count);
					assert(!lgl->mt);
					lglmt(lgl);
				}
			}
		}
	}
	res = lglcntstk(&card->units);
	lglrelstk(lgl, &card->units);
	if (!lgl->mt) {
		if (res)
			lglprt(lgl, 1, "[card-%d] found %d units", lgl->stats->card.count, res);
		else
			lglprt(lgl, 1, "[card-%d] no units found", lgl->stats->card.count);

		if (!lglmtstk(&card->expam1)) {
			int generated = 0, exported = 0, genused = 0;
			for (c = card->expam1.start; c < card->expam1.top; c = p + 1) {
				int a, usefull = 0;
				generated++;
				for (p = c; (a = *p); p++)
					if (lglval(lgl, a))
						break;
				if (a) {
					while (*++p)
						;
					continue;
				}
				for (p = c; (a = -*p); p++) {
					int b;
					for (q = p + 1; (b = -*q); q++)
						if (!lglhasbin(lgl, a, b)) {
							LOG(CARDLOGLEVEL, "exporting at-most-one constraint binary clause %d %d", a, b);
							assert(lglmtstk(&lgl->clause));
							lglpushstk(lgl, &lgl->clause, a);
							lglpushstk(lgl, &lgl->clause, b);
							lglpushstk(lgl, &lgl->clause, 0);
							lgladdcls(lgl, REDCS, 0, 1);
							lglclnstk(&lgl->clause);
							lgl->stats->card.expam1++;
							exported++;
							usefull = 1;
						}
				}
				if (usefull)
					genused++;
			}
			lglprt(lgl,
				   1,
				   "[card-%d] generated %d at-most-one constraints, %d used %.0f%%",
				   lgl->stats->card.count,
				   generated,
				   genused,
				   lglpcnt(genused, generated));
			lglprt(lgl,
				   1,
				   "[card-%d] exported %d binary clauses, %.1f per/constraint",
				   lgl->stats->card.count,
				   exported,
				   lglavg(exported, genused));
		} else
			lglprt(lgl, 1, "[card-%d] no at-most-one constraints generated", lgl->stats->card.count);
	}
	lglrelstk(lgl, &card->expam1);
	return res;
}

static int
lglcard(LGL* lgl)
{
	int success, count;
	int64_t limit;
	assert(!lgl->mt);
	lglstart(lgl, &lgl->times->card);
	lgl->stats->card.count++;
	if (lgl->level)
		lglbacktrack(lgl, 0);
	limit = lglsetcardlim(lgl);
	assert(!lgl->card);
	NEW(lgl->card, 1);
	count = lglcard1extract(lgl) + lglcard2extract(lgl);
	if (!lglterminate(lgl) && count) {
		lglsetcardlimagain(lgl, limit);
		success = lglcardelim(lgl, count);
	} else
		success = 0;
	lglrelstk(lgl, &lgl->card->atmost1);
	lglrelstk(lgl, &lgl->card->atmost2);
	DEL(lgl->card, 1);
	LGLUPDPEN(card, success);
	lglstop(lgl);
	return !lgl->mt;
}

/*------------------------------------------------------------------------*/

static int
lglbcaoccmin(LGL* lgl, int lit)
{
	int *p, *w, *eow;
	int res, blit, red, tag;
	HTS* hts;
	hts = lglhts(lgl, lit);
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	res = 0;
	for (p = w; res <= 3 && p < eow; p++) {
		blit = *p;
		tag = blit & MASKCS;
		red = blit & REDCS;
		if (tag == TRNCS || tag == LRGCS)
			p++;
		if (tag == LRGCS)
			continue;
		if (!red)
			res++;
	}
	return res;
}

static int
lglbcaskiplit(LGL* lgl, int lit)
{
	int pocc, nocc;
	if (lgl->opts->bca.val >= 2)
		return 0;
	pocc = lglbcaoccmin(lgl, lit);
	if (pocc <= 1)
		return 1;
	nocc = lglbcaoccmin(lgl, -lit);
	if (nocc <= 1)
		return 1;
	return pocc == 2 && nocc == 2;
}

static void
lglbcalitaux(LGL* lgl, int lit)
{
	int first, blit, tag, red, lidx, other, tmp, found, other2, found2, *r;
	const int *w, *p, *eow, *c, *q;
	Stk* covered = &lgl->bca->covered;
	int round;
	HTS* hts;
	assert(lglisfree(lgl, lit));
	assert(lgl->opts->bca.val);
	assert(!(lglavar(lgl, lit)->inred & (1 << (lit < 0))));
	hts = lglhts(lgl, lit);
	if (!hts->count)
		return;
	if (lglbcaskiplit(lgl, lit)) {
		lgl->stats->bca.skipped++;
		return;
	}
	assert(lglmtstk(covered));
	first = 1;
	w = lglhts2wchs(lgl, hts);
	eow = w + hts->count;
	for (round = 0; round < 2; round++) {
		for (p = w; (first || !lglmtstk(covered)) && p < eow; p++) {
			blit = *p;
			tag = blit & MASKCS;
			red = blit & REDCS;
			if (tag == TRNCS || tag == LRGCS)
				p++;
			assert(!red || tag != LRGCS);
			if (tag == LRGCS)
				continue;
			if (round != (tag == OCCS))
				continue;
			INCSTEPS(bca.steps);
			if (tag == BINCS) {
				assert(!round);
				other = blit >> RMSHFT;
				if (lglval(lgl, other) > 0)
					continue;
				assert(!lglval(lgl, other));
				found = first;
				while (!lglmtstk(covered))
					if (other == lglpopstk(covered))
						found = 1;
				if (found)
					lglpushstk(lgl, covered, other);
			} else if (tag == TRNCS) {
				assert(!round);
				other = blit >> RMSHFT;
				if (lglval(lgl, other) > 0)
					continue;
				other2 = *p;
				if (lglval(lgl, other2) > 0)
					continue;
				found = found2 = first;
				while (!lglmtstk(covered))
					if (other == (tmp = lglpopstk(covered)))
						found = 1;
					else if (tmp == other2)
						found2 = 1;
				if (found && !lglval(lgl, other))
					lglpushstk(lgl, covered, other);
				if (found2 && !lglval(lgl, other2))
					lglpushstk(lgl, covered, other2);
			} else {
				assert(!red);
				assert(round);
				assert(tag == OCCS);
				lidx = blit >> RMSHFT;
				c = lglidx2lits(lgl, red, lidx);
				INCSTEPS(bca.steps);
				for (q = c; (other = *q); q++)
					if (other != lit && lglval(lgl, other) > 0)
						break;
				if (other)
					continue;
				if (first) {
					for (q = c; (other = *q); q++)
						if (other != lit && !lglval(lgl, other))
							lglpushstk(lgl, covered, other);
				} else {
					for (q = covered->start; q < covered->top; q++)
						assert(!lglsignedmarked(lgl, *q)), lglsignedmark(lgl, *q);
					for (q = c; (other = *q); q++)
						if (other != lit && lglsignedmarked(lgl, other))
							assert(!lglval(lgl, other)), lglsignedunmark(lgl, other);
					r = covered->start;
					for (q = r; q < covered->top; q++)
						if (lglsignedmarked(lgl, (other = *q)))
							lglsignedunmark(lgl, other);
						else
							*r++ = other;
					covered->top = r;
				}
			}
			first = 0;
		}
	}
}

static int
lglbcalit(LGL* lgl, int lit)
{
	unsigned bit;
	AVar* av;
	int other;
	if (lgl->limits->bca.steps < lgl->stats->bca.steps)
		return 0;
	if (lgl->limits->bca.added < lgl->stats->bca.added)
		return 0;
	if (lglterminate(lgl))
		return 0;
	if (lglifrozen(lgl, lit))
		return 1;
	if (!lglisfree(lgl, lit))
		return 1;
	av = lglavar(lgl, lit);
	bit = (1 << (lit < 0));
	if (av->inred & bit)
		return 1;
	lglbcalitaux(lgl, lit);
	while (!lglmtstk(&lgl->bca->covered)) {
		INCSTEPS(bca.steps);
		other = lglpopstk(&lgl->bca->covered);
		if (lglhasbin(lgl, -lit, -other))
			continue;
		LOG(2, "adding binary blocked clause %d %d", -lit, -other);
		assert(lglmtstk(&lgl->clause));
		lglpushstk(lgl, &lgl->clause, -lit);
		lglpushstk(lgl, &lgl->clause, -other);
		lglpushstk(lgl, &lgl->clause, 0);
		lgladdcls(lgl, REDCS, 0, 1);
		lglclnstk(&lgl->clause);
		lgl->stats->bca.added++;
	}
	return 1;
}

static void
lglupdbcadel(LGL* lgl, int success)
{
	if (success && lgl->limits->bca.del.cur)
		lgl->limits->bca.del.cur /= 2;
	if (!success && lgl->limits->bca.del.cur < lgl->opts->delmax.val)
		lgl->limits->bca.del.cur++;
	lgl->limits->bca.del.rem = lgl->limits->bca.del.cur;
}

static void
lglbca(LGL* lgl)
{
	int oldadded = lgl->stats->bca.added, added, limadded;
	int idx, glue, other, inred, usable, scale;
	const int *c, *p;
	int64_t oldsteps, lim64;
	unsigned bit;
	AVar* av;

	lglstart(lgl, &lgl->times->bca);

	NEW(lgl->bca, 1);

	lgl->stats->bca.count++;
	oldsteps = lgl->stats->bca.steps;
	lgl->limits->bca.steps = oldsteps + lgl->opts->bcamaxeff.val;

	limadded = lgl->stats->irr.clauses.cur;
	scale = lgl->opts->bcaddlimldscale.val;
	if (scale < 0)
		limadded >>= -scale;
	else if ((INT_MAX >> scale) < limadded)
		limadded = INT_MAX;
	else
		limadded <<= scale;
	lim64 = lgl->stats->bca.added / 2;
	lim64 += limadded / 2 + 500;
	if (lim64 > (int64_t)INT_MAX)
		lim64 = INT_MAX;
	lgl->limits->bca.added = lim64;

	lglprt(lgl,
		   1,
		   "[bca-%d] limit of %d steps and %d added (scaled %d)",
		   lgl->stats->bca.count,
		   lgl->opts->bcamaxeff.val,
		   lgl->limits->bca.added,
		   scale);

	assert(!lgl->bcaing);
	lgl->bcaing = 1;

	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	lglgc(lgl);
	assert(lgl->frozen);

	for (idx = 2; idx < lgl->nvars; idx++)
		lglavar(lgl, idx)->inred = 0;
	inred = 0;
	for (glue = 0; glue <= MAXGLUE; glue++) {
		Stk* s = lgl->red + glue;
		for (c = s->start; c < s->top; c = p + 1) {
			p = c;
			if (*p >= NOTALIT)
				continue;
			INCSTEPS(bca.steps);
			while ((other = *p)) {
				p++;
				bit = (1 << (other < 0));
				av = lglavar(lgl, other);
				if (av->inred & bit)
					continue;
				av->inred |= bit;
				inred++;
			}
		}
	}
	lglprt(lgl,
		   2,
		   "[bca-%d] %d out of %d literals in large redundant clauses %.0f%%",
		   lgl->stats->bca.count,
		   inred,
		   2 * (lgl->nvars - 1),
		   lglpcnt(inred, 2 * (lgl->nvars - 1)));

	usable = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (lglifrozen(lgl, idx))
			continue;
		if (!lglisfree(lgl, idx))
			continue;
		av = lglavar(lgl, idx);
		for (bit = 1; bit <= 2; bit++)
			if (!(av->inred & bit))
				usable++;
	}
	lglprt(lgl,
		   1,
		   "[bca-%d] %d out of %d literals actually usable %.0f%%",
		   lgl->stats->bca.count,
		   usable,
		   2 * (lgl->nvars - 1),
		   lglpcnt(usable, 2 * (lgl->nvars - 1)));

	if (usable < lgl->opts->bcaminuse.val) {
		lglprt(lgl, 1, "[bca-%d] not enough literals usable", lgl->stats->bca.count);
		goto DONE;
	}

	lgldense(lgl, 0);
	lglrandlitrav(lgl, lglbcalit);
	lglsparse(lgl);
	lglgc(lgl);

DONE:
	added = lgl->stats->bca.added - oldadded;
	lglprt(lgl,
		   1,
		   "[bca-%d] added %d blocked clauses in %lld steps",
		   lgl->stats->bca.count,
		   added,
		   (LGLL)lgl->stats->bca.steps - oldsteps);
	assert(lgl->bcaing);
	lgl->bcaing = 0;
	lglupdbcadel(lgl, added);
	lglrelstk(lgl, &lgl->bca->covered);
	DEL(lgl->bca, 1);
	lglstop(lgl);
}

/*------------------------------------------------------------------------*/

static int
lgltreducing(LGL* lgl)
{
	if (lgldelaying(lgl, "transred", &lgl->limits->trd.del.rem))
		return 0;
	if (lglwaiting(lgl, "transred", lgl->opts->transredwait.val))
		return 0;
	return lgl->opts->transred.val;
}

static int
lglunhiding(LGL* lgl)
{
	if (lgldelaying(lgl, "unhide", &lgl->limits->unhd.del.rem))
		return 0;
	if (lglwaiting(lgl, "unhide", lgl->opts->unhidewait.val))
		return 0;
	return lgl->opts->unhide.val;
}

static int
lgldecomposing(LGL* lgl)
{
	return lgl->opts->decompose.val;
}

static int
lglblocking(LGL* lgl)
{
	if (!lgl->opts->block.val)
		return 0;
	if (lgl->allfrozen)
		return 0;
	if (lgldelaying(lgl, "block", &lgl->limits->blk.del.rem))
		return 0;
	if (lgl->opts->blockwait.val && lgl->wait && (!lgl->opts->elim.val || !lgl->elmrtc)) {
		lglprt(lgl, 2, "[block-waiting] for bounded variable elimination to be completed");
		return 0;
	}
	if (!lglsmallirr(lgl))
		return 0;
	if (lgl->nvars <= 2)
		return 0;
	if (lgl->mt)
		return 0;
	if (lgl->blkrem)
		return 1;
	return lgl->stats->irrprgss > lgl->limits->blk.irrprgss;
}

static int
lgleliminating(LGL* lgl)
{
	if (!lgl->opts->elim.val)
		return 0;
	if (lgl->allfrozen)
		return 0;
	if (lgldelaying(lgl, "elim", &lgl->limits->elm.del.rem))
		return 0;
	if (!lglsmallirr(lgl))
		return 0;
	if (lgl->nvars <= 2)
		return 0;
	if (lgl->mt)
		return 0;
	if (lgl->elmrem)
		return 1;
	return lgl->stats->irrprgss > lgl->limits->elm.irrprgss;
}

static int
lglbcaing(LGL* lgl)
{
	if (!lgl->opts->bca.val)
		return 0;
	if (lgl->allfrozen)
		return 0;
	if (lgldelaying(lgl, "bca", &lgl->limits->bca.del.rem))
		return 0;
	if (lglwaiting(lgl, "bca", lgl->opts->bcawait.val))
		return 0;
	if (!lglsmallirr(lgl))
		return 0;
	return 1;
}

static int
lglreducing(LGL* lgl)
{
	int reducable;
	if (!lgl->opts->reduce.val)
		return 0;
	reducable = lgl->stats->red.lrg;
	assert(reducable >= lgl->lrgluereasons);
	reducable -= lgl->lrgluereasons;
	assert(reducable >= lgl->stats->lir[0].clauses);
	reducable -= lgl->stats->lir[0].clauses;
	return reducable >= lgl->limits->reduce.redlarge;
}

static int
lgldefragmenting(LGL* lgl)
{
	int relfree;
	if (lgl->stats->pshwchs < lgl->limits->dfg)
		return 0;
	if (!lgl->nvars)
		return 0;
	relfree = (100 * lgl->wchs->free + 99) / lgl->nvars;
	return relfree >= lgl->opts->defragfree.val;
}

static int
lglforcerestart(LGL* lgl)
{
	const int64_t percent = lgl->opts->restartforcelim.val;
	int64_t actual, smoothed, limit;
	int forcing;
	assert(lgl->opts->restartcheckforced.val);
	assert(percent);
	switch (lgl->opts->restartforcemode.val) {
		default:
		case 0:
			actual = lgl->stats->glue.fast.val;
			smoothed = lgl->stats->avglue.val;
			break;
		case 1:
			actual = lgl->stats->glue.fast.val;
			smoothed = lgl->stats->glue.slow.val;
			break;
		case 2:
			actual = lgl->stats->glue.diff.actual;
			smoothed = lgl->stats->glue.diff.smoothed.val;
			break;
	}
	assert(lglvalidint64(smoothed));
	limit = (smoothed < 0 ? -smoothed : smoothed);
	assert(limit >= 0);
	if (!lglvalidint64(limit))
		return 0;
	limit /= 100;
	if (INT64MAX / percent < limit)
		return 0;
	limit *= percent;
	forcing = (limit < actual);
	LOG(2,
		"%s glue MACD %g * %.2f = %g %s %g at %lld conflicts",
		(forcing ? "forcing" : "not forcing"),
		smoothed / (double)(1ll << 32),
		lgl->opts->restartforcelim.val / 100.0,
		limit / (double)(1ll << 32),
		forcing ? "<" : ">=",
		actual / (double)(1ll << 32),
		(LGLL)lgl->stats->confs);
	return forcing;
}

static int
lgldelayrestart(LGL* lgl)
{
	const int64_t actual = ((int64_t)lgl->level) << 32;
	const int64_t smoothed = lgl->stats->jlevel.slow.val;
	const int64_t percent = lgl->opts->restartdelaylim.val;
	int64_t limit;
	int delaying;
	assert(lgl->opts->restartdelay.val);
	assert(percent);
	assert(lglvalidint64(smoothed));
	limit = (smoothed < 0 ? -smoothed : smoothed);
	assert(limit >= 0);
	if (!lglvalidint64(limit))
		return 0;
	limit /= 100;
	if (INT64MAX / percent < limit)
		return 0;
	limit *= percent;
	delaying = (limit > actual);
	LOG(2,
		"%s restart MACD %g * %.2f = %g %s %g at %lld conflicts",
		(delaying ? "delaying" : "not delaying"),
		smoothed / (double)(1ll << 32),
		lgl->opts->restartdelaylim.val / 100.0,
		limit / (double)(1ll << 32),
		delaying ? ">" : "<=",
		actual / (double)(1ll << 32),
		(LGLL)lgl->stats->confs);
	return delaying;
}

static int
lglrestarting(LGL* lgl)
{
	int assumptions;
	if (lgl->stats->confs < lgl->limits->restart.confs)
		return 0;
	if (!lgl->opts->restart.val)
		return 0;
	if (!lgl->level)
		return 0;
	if ((assumptions = lglcntstk(&lgl->assume))) {
		if (lgl->assumed < assumptions)
			return 0;
		assert(lgl->alevel <= lgl->level);
		if (lgl->alevel == lgl->level)
			return 0;
	}
	lgl->stats->restarts.checked++;
	lgl->limits->restart.confs = lgl->stats->confs + 1;
	if (lgl->opts->restartfixed.val)
		return 1;
	if (lgl->opts->agility.val && ((100 * lgl->stats->agility) >> 32) > lgl->opts->agilitylim.val) {
		lgl->stats->restarts.agile++;
	NORESTART:
		lgl->stats->restarts.skipped++;
		lglrep(lgl, 3, 'L');
		return 0;
	}
	if (lgl->opts->restartdelay.val && lgldelayrestart(lgl)) {
		lgl->stats->restarts.delayed++;
		goto NORESTART;
	}
	if (lgl->opts->restartblock.val == 2 && lglblockrestart(lgl)) {
		lgl->limits->restart.confs = lgl->stats->confs + lgl->opts->restartint.val;
		lgl->stats->restarts.blocked++;
		goto NORESTART;
	}
	if (lgl->opts->restartcheckforced.val && !lglforcerestart(lgl)) {
		lgl->stats->restarts.notforced++;
		goto NORESTART;
	}
	lgl->stats->restarts.forced++;
#ifndef NLGLFILES
	{
		static FILE* restartfile = 0;
		if (!restartfile)
			restartfile = fopen("/tmp/restarts", "w");
		fprintf(restartfile, "%lld\n", (LGLL)lgl->stats->confs);
		fflush(restartfile);
	}
#endif
	return 1;
}

/*------------------------------------------------------------------------*/

static int
lglpruning(LGL* lgl)
{
	if (lgl->mt)
		return 0;
	if (!lgl->unassigned)
		return 0;
	if (!lgl->opts->prune.val)
		return 0;
	if (lgl->opts->drupligcheck.val)
		return 0;
	if (!lglmtstk(&lgl->assume))
		return 0;
	assert(!lgl->alevel);
	if (!lgl->level)
		return 0;
	if (lgl->level > lgl->opts->prunelevel.val)
		return 0;
	return lgl->limits->prune.decisions <= lgl->stats->decisions;
}

static void
lglictrav(LGL* lgl, int internal, int redtoo, void* state, void (*trav)(void*, int red, int lit));

struct PruneState
{
	LGL* parent;
	LGL* child;
	Stk clause;
	int* map;
	int nvars;
	int red;
	int irr;
};

typedef struct PruneState PruneState;

static void
lglpruneaddcls(PruneState* state, int red)
{
	int sat = 0, nfalse = 0, ntrue = 0, nunassigned = 0, size = 0;
	const int* p;
	int* q;
#ifndef NLGLOG
	LGL* lgl = state->parent;
	int origsize = lglcntstk(&state->clause);
	const char* redstr = red ? "redundant" : "irredundant";
	lglpushstk(state->parent, &state->clause, 0);
	lglpopstk(&state->clause);
	LOGCLS(4, state->clause.start, "trying to add parent %s size %d clause", redstr, origsize);
#endif
	for (p = state->clause.start; !sat && p < state->clause.top; p++) {
		const int plit = *p;
		int tmp = lglifixed(state->parent, plit);
		if (tmp < 0) {
			LOG(3, "ignoring root-level falsified literal %d", plit);
		} else if (tmp > 0) {
			LOG(3, "found root-level satisfied literal %d", plit);
			sat = 1;
		} else {
			tmp = lglval(state->parent, plit);
			if (tmp < 0)
				nfalse++;
			else if (tmp > 0)
				ntrue++;
			else
				nunassigned++;
			size++;
		}
	}
	if (sat) {
		LOGCLS(3, state->clause.start, "ignoring satisfied parent %s size %d clause", redstr, origsize);
	} else if (size == nunassigned) {
		LOGCLS(3, state->clause.start, "ignoring completely unassigned parent %s size %d clause", redstr, origsize);
	} else if (!ntrue) {
		LOGCLS(3,
			   state->clause.start,
			   "ignoring parent %s size %d clause "
			   "without true but %d false literals",
			   redstr,
			   origsize,
			   nfalse);
	} else {
		assert(size > 0);
		assert(ntrue > 0);
		LOGCLS(3,
			   state->clause.start,
			   "copying parent %s original size %d clause "
			   "with %d true and %d false literals and %d skipped",
			   redstr,
			   origsize,
			   ntrue,
			   nfalse,
			   nunassigned);
		q = state->clause.start;
		for (p = q; p < state->clause.top; p++) {
			int plit = *p, tmp = lglval(state->parent, plit);
			if (tmp && !lglifixed(state->parent, plit)) {
				int pidx = abs(plit), cidx = state->map[pidx], clit;
				if (!cidx) {
					cidx = ++state->nvars;
					LOG(3,
						"mapping parent internal variable %d "
						"to child external variable %d",
						pidx,
						cidx);
					state->map[pidx] = cidx;
				}
				clit = plit < 0 ? -cidx : cidx;
				*q++ = clit;
			}
		}
		state->clause.top = q;
#ifndef NLGLOG
		size = lglcntstk(&state->clause);
		lglpushstk(state->parent, &state->clause, 0);
		lglpopstk(&state->clause);
		LOGCLS(4, state->clause.start, "add child %s size %d external clause", redstr, size);
#endif
		if (red)
			state->red++;
		else
			state->irr++;
		for (p = state->clause.start; p < state->clause.top; p++)
			lgladd(state->child, *p);
		lgladd(state->child, 0);
	}
	lglclnstk(&state->clause);
}

static int
lglprunered(LGL* lgl)
{
	if (lgl->opts->block.val)
		return 1;
	if (lgl->opts->prunered.val)
		return 1;
	return 0;
}

static void
lglpruneadd(void* ptr, int red, int lit)
{
	PruneState* state = ptr;
	if (red && !lglprunered(state->parent))
		return;
	if (lit)
		lglpushstk(state->parent, &state->clause, lit);
	else
		lglpruneaddcls(state, red);
}

static void
lglpruneana(PruneState* state)
{
	LGL* parent = state->parent;
#ifndef NLGLDRUPLIG
	LGL* child = state->child;
	int first = 0;
#endif
#ifndef NLGLOG
	LGL* lgl = parent;
#endif
	int len;
	Ctr* c;
	assert(parent->level - parent->alevel > 0);
	assert(lglmtstk(&parent->seen));
	assert(lglmtstk(&parent->clause));
	assert(!parent->opts->drupligcheck.val);
	LOG(2, "analyzing and pruning level %d clause", parent->level);
	for (c = parent->control.start + 1; c < parent->control.top; c++) {
		int decision = c->decision;
		lglpushstk(parent, &parent->clause, -decision);
		lglpushstk(parent, &parent->seen, decision);
#ifndef NLGLDRUPLIG
		if (lgldruplig(parent)) {
			if (!first) {
				int pidx = abs(decision);
				int cidx = state->map[pidx];
				if (cidx) {
					int clit = (decision < 0 ? -cidx : cidx);
					int tmp = lglderef(child, clit);
					assert(tmp);
					if (tmp < 0) {
						first = -decision;
						LOG(3, "first (parent internal) literal %d", first);
					}
				}
			}
			druplig_add_literal(parent->druplig, lglexport(parent, -decision));
		}
#endif
	}
#ifndef NLGLDRUPLIG
	if (lgldruplig(parent)) {
		int found = 0, pidx;
		assert(first);
		druplig_add_literal(parent->druplig, lglexport(parent, first));
		for (pidx = 2; pidx < parent->nvars; pidx++) {
			int cidx = state->map[pidx], tmp, lit;
			if (!cidx)
				continue;
			tmp = lglderef(child, cidx);
			assert(tmp);
			lit = (tmp < 0) ? -pidx : pidx;
			assert(first != -lit);
			if (first == lit)
				found++;
			else
				druplig_add_literal(parent->druplig, lglexport(parent, lit));
		}
		lgldrupligreallyadd(parent, REDCS);
		assert(found == 1), (void)found;
	}
#endif
	lglbumpseenlits(parent);
	len = parent->level;
	assert(len == (int)lglcntstk(&parent->clause));
	parent->stats->lits.nonmin += len;
	parent->stats->clauses.glue += len - 1;
	parent->stats->clauses.realglue += len - 1;
	parent->stats->lits.learned += len;
	parent->stats->clauses.learned++;
	parent->stats->mincls.size++;
	parent->stats->decflipped++;
	parent->stats->confs++;
	lglbacktrack(parent, len - 1);
	lglpushstk(parent, &parent->clause, 0);
	(void)lgladdcls(parent, (lglprunered(parent) ? REDCS : 0), len - 1, 1);
	lglrndcls(parent, len, 1);
	lglclnstk(&parent->clause);
	lglclnstk(&parent->seen);
}

static void
lglsetpruneprefix(LGL* child, LGL* parent, int level)
{
	int len = strlen(parent->prefix);
	int allocated = len + 80;
	char* prefix = lglnew(child, allocated);
	char buffer[80];
	sprintf(
		buffer, "[prune-%lld-%lld-%d] .. ", (LGLL)parent->stats->prune.decs, (LGLL)parent->stats->prune.calls, level);
	strcpy(prefix, parent->prefix);
	if (len && child->prefix[len - 1] != ' ')
		strcat(prefix, " ");
	strcat(prefix, buffer);
	lgldelstr(child, child->prefix);
	child->prefix = lglstrdup(child, prefix);
	lgldel(child, prefix, allocated);
}

static void
lglupdprunelim(LGL* lgl, int success)
{
	if (success) {
		lgl->limits->prune.inc /= 2;
		if (lgl->limits->prune.inc < lgl->opts->prunemin.val)
			lgl->limits->prune.inc = lgl->opts->prunemin.val;
	} else {
		int64_t inc = lgl->limits->prune.inc / 10;
		if (!inc)
			inc = 1;
		lgl->limits->prune.inc += inc;
		if (lgl->limits->prune.inc > lgl->opts->prunemax.val)
			lgl->limits->prune.inc = lgl->opts->prunemax.val;
	}
	lgl->limits->prune.decisions = lgl->stats->decisions + lgl->limits->prune.inc;
	lglprt(lgl,
		   2,
		   "[prune-%lld-%lld] %s new decision increment %lld limit %lld",
		   (LGLL)lgl->stats->prune.decs,
		   (LGLL)lgl->stats->prune.calls,
		   success ? "successful" : "unsuccessful",
		   (LGLL)lgl->limits->prune.inc,
		   (LGLL)lgl->limits->prune.decisions);
}

#if 1

#if 0

static int lglprunedecidefirst (LGL * lgl) {
  int valit, valother, valother2, decision, pos, neg, level, glue, sat;
  int idx, sign, lit, blit, tag, red, other, other2, size, pure, tmp;
  const int skipred = !lglprunered (lgl);
  const int * p, * w, * eow, * c;
  int * bincount, * count;
  long best, score;
  HTS * hts;
  Stk * s;
  NEW (count, 2*lgl->nvars);
  NEW (bincount, 2*lgl->nvars);
  bincount += lgl->nvars;
  count += lgl->nvars;
  level = lgl->level - lgl->alevel;
  assert (!level);
  for (idx = 2; idx < lgl->nvars; idx++) {
    for (sign = -1; sign <= 1; sign += 2) {
      lit = sign * idx;
      valit = lglval (lgl, lit);
      if (valit > 0) continue;
      hts = lglhts (lgl, lit);
      w = lglhts2wchs (lgl, hts);
      eow = w + hts->count;
      for (p = w; p < eow; p++) {
	blit = *p;
	tag = blit & MASKCS;
	if (tag == TRNCS || tag == LRGCS) p++;
	if (tag == LRGCS) continue;
	red = blit & REDCS;
	if (skipred && red) continue;
	other = blit >> RMSHFT;
	if (abs (other) < idx) continue;
	other2 = (tag == BINCS) ? 0 : *p;
	if (other2 && abs (other2) < idx) continue;
	valother = lglval (lgl, other);
	if (valother > 0) continue;
	valother2 = (other2 ? lglval (lgl, other2) : -1);
	if (valother2 > 0) continue;
	size = 0;
	if (!valit) size++;
	if (!valother) size++;
	if (!valother2) size++;
	assert (size > 1);
	if (!valit) count[lit]++;
	if (!valother) count[other]++;
	if (!valother2) count[other2]++;
	if (size > 2) continue;
	if (!valit) bincount[lit]++;
	if (!valother) bincount[other]++;
	if (!valother2) bincount[other2]++;
      }
    }
  }

  for (glue = -1; glue < MAXGLUE; glue++) {
    if (glue >= 0 && skipred) break;
    s = glue < 0 ? &lgl->irr : &lgl->red[glue];
    for (c = s->start; c < s->top; c = p + 1) {
      p = c;
      if (*p >= NOTALIT) continue;
      size = sat = 0;
      while ((other = *p)) {
	tmp = lglval (lgl, other);
	if (tmp > 0) sat = 1;
	if (!tmp) size++;
	p++;
      }
      if (sat) continue;
      assert (size > 1);
      p = c;
      while ((other = *p)) {
	tmp = lglval (lgl, other);
	if (!tmp) {
	  if (size == 2) bincount[other]++;
	  count[other]++;
	}
	p++;
      }
    }
  }

  pure = 0;
  for (idx = 2; !pure && !skipred && idx < lgl->nvars; idx++) {
    if (lglval (lgl, idx)) continue;
    if (lglifrozen (lgl, idx)) continue;
    for (sign = -1; !pure && sign <= 2; sign += 2) {
      lit = sign * idx;
      tmp = count[-lit];
      if (!tmp) pure = lit;
    }
  }

  if (pure && !lgl->opts->drupligcheck.val) {
    decision = INT_MIN;
    lglprt (lgl, 2,
      "[prune-%lld-%lld-%d] pure literal %d in %d clauses",
      (LGLL) lgl->stats->prune.decs,
      (LGLL) lgl->stats->prune.calls,
      level+1,
      pure, count[pure]);
    lgl->stats->prune.pure++;
    lglunit (lgl, pure);
  } else {
    decision = 0, best = LLMAX;
    for (idx = 2; idx < lgl->nvars; idx++) {
      if (lglval (lgl, idx)) continue;
      if (lglifrozen (lgl, idx)) continue;
      pos = bincount[idx];
      neg = bincount[-idx];
      score = pos * (long) neg + pos + neg;
      if (score >= best) continue;
      decision = (pos < neg ? idx : -idx);
      best = score;
    }

    lglprt (lgl, 2,
      "[prune-%lld-%lld-%d] "
      "best decision %d with %d positive and %d negated binary occurrences",
      (LGLL) lgl->stats->prune.decs,
      (LGLL) lgl->stats->prune.calls,
      level+1,
      decision, bincount[decision], bincount[-decision]);
  }

  count -= lgl->nvars;
  bincount -= lgl->nvars;
  DEL (bincount, 2*lgl->nvars);
  DEL (count, 2*lgl->nvars);

  return decision;
}

#else

static int
lglprunedecidefirst(LGL* lgl)
{
	int idx, sign, lit, blit, tag, red, other, other2, size, pure, tmp;
	int valit, valother, valother2, decision, glue, sat;
	const int skipred = !lglprunered(lgl);
	const int *p, *w, *eow, *c;
	int *count, bestcount;
	int64_t *sum, bestsum;
	HTS* hts;
	Stk* s;
	NEW(count, 2 * lgl->nvars);
	NEW(sum, 2 * lgl->nvars);
	count += lgl->nvars;
	sum += lgl->nvars;
	assert(!lgl->level);
	for (idx = 2; idx < lgl->nvars; idx++) {
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			valit = lglval(lgl, lit);
			if (valit > 0)
				continue;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (tag == LRGCS)
					continue;
				red = blit & REDCS;
				if (skipred && red)
					continue;
				other = blit >> RMSHFT;
				if (abs(other) < idx)
					continue;
				other2 = (tag == BINCS) ? 0 : *p;
				if (other2 && abs(other2) < idx)
					continue;
				valother = lglval(lgl, other);
				if (valother > 0)
					continue;
				valother2 = (other2 ? lglval(lgl, other2) : -1);
				if (valother2 > 0)
					continue;
				size = 0;
				if (!valit)
					size++;
				if (!valother)
					size++;
				if (!valother2)
					size++;
				assert(size > 1);
				if (!valit)
					count[lit]++, sum[lit] += size;
				if (!valother)
					count[other]++, sum[other] += size;
				if (!valother2)
					count[other2]++, sum[other2] += size;
			}
		}
	}

	for (glue = -1; glue < MAXGLUE; glue++) {
		if (glue >= 0 && skipred)
			break;
		s = glue < 0 ? &lgl->irr : &lgl->red[glue];
		for (c = s->start; c < s->top; c = p + 1) {
			p = c;
			if (*p >= NOTALIT)
				continue;
			size = sat = 0;
			while ((other = *p)) {
				tmp = lglval(lgl, other);
				if (tmp > 0)
					sat = 1;
				if (!tmp)
					size++;
				p++;
			}
			if (sat)
				continue;
			assert(size > 1);
			p = c;
			while ((other = *p)) {
				tmp = lglval(lgl, other);
				if (!tmp)
					count[other]++, sum[other] += size;
				p++;
			}
		}
	}

	pure = 0;
	if (lgl->opts->prunepure.val && !lgl->opts->drupligcheck.val && !skipred) {
		for (idx = 2; !pure && idx < lgl->nvars; idx++) {
			if (lglval(lgl, idx))
				continue;
			if (lglifrozen(lgl, idx))
				continue;
			for (sign = 1; !pure && sign >= -1; sign -= 2)
				if (!count[lit = sign * idx])
					pure = -lit;
		}
	}

	if (pure) {
		decision = INT_MIN;
		lglprt(lgl,
			   2,
			   "[prune-%lld-%lld-%d] found pure literal %d in %d clauses",
			   (LGLL)lgl->stats->prune.decs,
			   (LGLL)lgl->stats->prune.calls,
			   lgl->level + 1,
			   pure,
			   count[pure]);
		lgl->stats->prune.pure++;
		lglunit(lgl, pure);
	} else {
		decision = 0, bestsum = 0, bestcount = INT_MAX;
		for (idx = 2; idx < lgl->nvars; idx++) {
			if (lglval(lgl, idx))
				continue;
			if (lglifrozen(lgl, idx))
				continue;
			for (sign = 1; !pure && sign >= -1; sign -= 2) {
				int tmpcount, update = 0;
				;
				int64_t tmpsum;
				lit = sign * idx;
				tmpcount = count[lit];
				tmpsum = sum[lit];
				if (tmpcount < bestcount)
					update = 1;
				else if (tmpcount && tmpcount == bestcount) {
					int64_t L, R;
					if (LLMAX / tmpcount <= bestsum)
						L = LLMAX;
					else
						L = bestsum * tmpcount;
					if (LLMAX / bestcount <= tmpsum)
						R = LLMAX;
					else
						R = tmpsum * bestcount;
					update = L < R;
				}
				if (!update)
					continue;
				decision = lit;
				bestcount = tmpcount;
				bestsum = tmpsum;
			}
		}

		lglprt(lgl,
			   2,
			   "[prune-%lld-%lld-%d] "
			   "best decision %d with %d occurrences and average length %.3f",
			   (LGLL)lgl->stats->prune.decs,
			   (LGLL)lgl->stats->prune.calls,
			   lgl->level + 1,
			   decision,
			   count[decision],
			   lglavg(sum[decision], count[decision]));
	}

	sum -= lgl->nvars;
	count -= lgl->nvars;
	DEL(sum, 2 * lgl->nvars);
	DEL(count, 2 * lgl->nvars);

	return decision;
}

#endif

static int
lglprunedecidelater(LGL* lgl)
{
	int decision, shortest, size, tmp, sat, reduced, lit, glue;
	int idx, sign, blit, tag, red, other, other2;
	const int skipred = !lglprunered(lgl);
	int valit, valother, valother2;
	const int *p, *w, *eow, *c;
	HTS* hts;
	Stk* s;

	assert(lgl->level);
	assert(lgl->level < lgl->opts->prunelevel.val);

	decision = 0, shortest = INT_MAX;

	for (idx = 2; idx < lgl->nvars; idx++) {
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			valit = lglval(lgl, lit);
			if (valit > 0)
				continue;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == BINCS)
					continue;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (tag == LRGCS)
					continue;
				red = blit & REDCS;
				if (skipred && red)
					continue;
				other = blit >> RMSHFT;
				if (abs(other) < idx)
					continue;
				valother = lglval(lgl, other);
				if (valother > 0)
					continue;
				other2 = *p;
				if (abs(other2) < idx)
					continue;
				valother2 = lglval(lgl, other2);
				if (valother2 > 0)
					continue;
				size = 0;
				if (!valit)
					size++;
				if (!valother)
					size++;
				if (!valother2)
					size++;
				assert(size > 1);
				if (size > 2)
					continue;
				assert(size == 2);
				tmp = 0;
				if (!valit && !lglifrozen(lgl, lit))
					tmp = lit;
				if (!valother && !lglifrozen(lgl, other))
					tmp = other;
				if (!valother2 && !lglifrozen(lgl, other2))
					tmp = other2;
				if (!tmp || abs(tmp) >= abs(decision))
					continue;
				decision = tmp;
				shortest = 2;
			}
		}
	}

	if (!decision) {
		for (glue = -1; glue < MAXGLUE; glue++) {
			if (glue >= 0 && skipred)
				break;
			s = glue < 0 ? &lgl->irr : &lgl->red[glue];
			for (c = s->start; c < s->top; c = p + 1) {
				p = c;
				if (*p >= NOTALIT)
					continue;
				reduced = sat = lit = size = 0;
				while ((other = *p)) {
					tmp = lglval(lgl, other);
					if (tmp > 0)
						sat = 1;
					if (!tmp) {
						if (!lglifrozen(lgl, other) && (!lit || abs(other) < abs(lit)))
							lit = other;
						size++;
					} else if (lglevel(lgl, other))
						reduced = 1;
					p++;
				}
				if (sat || !lit || !reduced)
					continue;
				assert(size > 1);
				if (size > shortest)
					continue;
				if (size == shortest && abs(lit) >= abs(decision))
					continue;
				assert(lit);
				decision = lit;
				shortest = size;
			}
		}
	}

	lglprt(lgl,
		   2,
		   "[prune-%lld-%lld-%d] "
		   "best decision %d in shortest reduced clause of size %d",
		   (LGLL)lgl->stats->prune.decs,
		   (LGLL)lgl->stats->prune.calls,
		   lgl->level + 1,
		   decision,
		   shortest);

	return decision;
}

#else

static void
lglprunedecidecands(LGL* lgl, char* cands)
{
	int size, reduced, sat, valit, valother, valother2, count, glue;
	int blit, tag, red, other, other2, idx, sign, lit, tmp;
	const int skipred = !lglprunered(lgl);
	const int *p, *w, *eow, *c;
	HTS* hts;
	Stk* s;
	for (idx = 2; idx < lgl->nvars; idx++)
		cands[idx] = cands[-idx] = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			valit = lglval(lgl, lit);
			if (valit > 0)
				continue;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == BINCS && lgl->level)
					continue;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (tag == LRGCS)
					continue;
				red = blit & REDCS;
				if (skipred && red)
					continue;
				other = blit >> RMSHFT;
				if (abs(other) < idx)
					continue;
				valother = lglval(lgl, other);
				if (valother > 0)
					continue;
				if (tag == TRNCS) {
					other2 = *p;
					if (abs(other2) < idx)
						continue;
					valother2 = lglval(lgl, other2);
					if (valother2 > 0)
						continue;
					size = 0;
					if (!valit)
						size++;
					if (!valother)
						size++;
					if (!valother2)
						size++;
					assert(size > 1);
					if (size > 2)
						continue;
					assert(size == 2);
					if (lgl->level) {
						reduced = 0;
						if (valit < 0 && lglevel(lgl, lit))
							reduced++;
						if (valother < 0 && lglevel(lgl, valother))
							reduced++;
						if (valother2 < 0 && lglevel(lgl, valother2))
							reduced++;
						if (!reduced)
							continue;
					}
					if (!valit && !lglifrozen(lgl, lit))
						cands[lit] = 1;
					if (!valother && !lglifrozen(lgl, other))
						cands[other] = 1;
					if (!valother2 && !lglifrozen(lgl, other2))
						cands[other2] = 1;
				} else {
					assert(!valit);
					assert(!valother);
					if (!lglifrozen(lgl, lit))
						cands[lit] = 1;
					if (!lglifrozen(lgl, other))
						cands[other] = 1;
				}
			}
		}
	}
	for (glue = -1; glue < MAXGLUE; glue++) {
		if (glue >= 0 && skipred)
			break;
		s = glue < 0 ? &lgl->irr : &lgl->red[glue];
		for (c = s->start; c < s->top; c = p + 1) {
			p = c;
			if (*p >= NOTALIT)
				continue;
			reduced = sat = size = 0;
			while ((other = *p)) {
				tmp = lglval(lgl, other);
				if (tmp > 0)
					sat++;
				if (!tmp && !lglifrozen(lgl, other))
					size++;
				if (tmp < 0 && lglevel(lgl, other))
					reduced++;
				p++;
			}
			if (sat || !size)
				continue;
			if (lgl->level && !reduced)
				continue;
			p = c;
			while ((other = *p)) {
				tmp = lglval(lgl, other);
				if (!tmp && !lglifrozen(lgl, other))
					cands[other] = 1;
				p++;
			}
		}
	}
	count = 0;
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2)
			if (cands[sign * idx])
				count++;
	lglprt(lgl,
		   2,
		   "[prune-%lld-%lld-%d] "
		   "found %d decision candidates at level %d",
		   (LGLL)lgl->stats->prune.decs,
		   (LGLL)lgl->stats->prune.calls,
		   lgl->level + 1,
		   count,
		   lgl->level);
}

static int
lglprunedecidesize(LGL* lgl, char* cands, int size, int* nextsizeptr)
{
	int nextsize = INT_MAX;
	*nextsizeptr = nextsize;
	return 0;
}

static int
lglprunedecidephase(LGL* lgl, int idx)
{
	return idx;
}

static int
lglprunedecidelit(LGL* lgl)
{
	int size, next, idx = 0, res;
	char* cands;
	NEW(cands, 2 * lgl->nvars);
	cands += lgl->nvars;
	lglprunedecidecands(lgl, cands);
	for (size = 2; !idx && size != INT_MAX && size < lgl->opts->prunesize.val; size = next)
		idx = lglprunedecidesize(lgl, cands, size, &next);
	cands -= lgl->nvars;
	DEL(cands, 2 * lgl->nvars);
	res = lglprunedecidephase(lgl, idx);
	return res;
}

#define lglprunedecidefirst lglprunedecidelit
#define lglprunedecidelater lglprunedecidelit

#endif

static int
lglprunedecide(LGL* lgl)
{
	int decision;
	if (lgl->mt)
		return 0;
	if (!lgl->unassigned)
		return 0;
	if (!lgl->opts->prune.val)
		return 0;
	if (lgl->opts->prunevsids.val)
		return 0;
	if (!lglmtstk(&lgl->assume))
		return 0;
	if (lgl->level >= lgl->opts->prunelevel.val)
		return 0;
	lglstart(lgl, &lgl->times->prune);
	lgl->stats->prune.decs++;
	if (!lgl->level)
		decision = lglprunedecidefirst(lgl);
	else
		decision = lglprunedecidelater(lgl);
	LOG(2, "prune decision %d", decision);
	lglstop(lgl);
	return decision;
}

static void
lglprunecopytrail(LGL* parent, int* map, LGL* child)
{
#ifndef NLGLOG
	LGL* lgl = parent;
#endif
	const int* p;
	int size = 0;
	for (p = parent->trail.start; p < parent->trail.top; p++) {
		int plit = *p, level = lglevel(parent, plit), pidx, cidx;
		assert(lglval(parent, plit) > 0);
		if (level && !lgldecision(parent, plit))
			continue;
		pidx = abs(plit);
		cidx = map[pidx];
		if (cidx) {
			int clit = plit < 0 ? -cidx : cidx;
			if (level) {
				LOG(3, "adding parent decision %d negated as child external literal %d", plit, -clit);
				lgladd(child, -clit);
				size++;
			} else {
				LOG(3, "adding parent unit %d as external child unit %d", plit, clit);
				lgladd(child, clit);
				lgladd(child, 0);
			}
		} else
			LOG(2, "ignoring unused parent literal %d", plit);
	}
	lgladd(child, 0);
	LOG(2, "added %d negated decisions", size);
	assert(size <= parent->level);
}

static LGL*
lglprunechild(LGL* parent, int level, PruneState* state)
{
	LGL* child;
	assert(0 < level);
	assert(parent->level - parent->alevel == level);
	assert(level <= parent->opts->prunelevel.val);
	child = lglminit(parent->mem->state, parent->mem->alloc, parent->mem->realloc, parent->mem->dealloc);
	lglcopyclonenfork(child, parent);
	lglsetpruneprefix(child, parent, level);
	child->opts->prune.val = 0;
	child->opts->clim.val = parent->opts->pruneclim.val;
	child->opts->trep.val = 0;
	child->opts->druplig.val = 0;
	child->opts->drupligcheck.val = 0;
	child->opts->drupligtrace.val = 0;
	child->opts->drupligtraceorig.val = 0;
	if (child->opts->verbose.val > -1 && parent->opts->log.val < 0)
		child->opts->verbose.val--;
	if (!child->opts->plain.val)
		lglsetplain(child, 1);
	CLR(*state);
	state->parent = parent;
	state->child = child;
	state->map = lglnew(parent, parent->nvars * sizeof *state->map);
	lglictrav(parent, 1, 1, state, lglpruneadd);
	lglrelstk(parent, &state->clause);
	lglprt(parent,
		   2,
		   "[prune-%lld-%lld-%d] "
		   "copied %d variables in %d irredundant and %d redundant clauses",
		   (LGLL)parent->stats->prune.decs,
		   (LGLL)parent->stats->prune.calls,
		   level,
		   state->nvars,
		   state->irr,
		   state->red);
	lglprunecopytrail(parent, state->map, child);
	return child;
}

static int
lglprunesat(LGL* parent, int level)
{
	PruneState state;
	LGL* child = lglprunechild(parent, level, &state);
	int res = lglsat(child);
	parent->stats->prune.child.confs += child->stats->confs;
	parent->stats->prune.child.decs += child->stats->decisions;
	parent->stats->prune.child.props += child->stats->props.search;
	parent->stats->prune.child.props += child->stats->props.simp;
	lglprt(parent,
		   2,
		   "[prune-%lld-%lld-%d] result %d in %lld conflicts and %lld decisions",
		   (LGLL)parent->stats->prune.decs,
		   (LGLL)parent->stats->prune.calls,
		   level,
		   res,
		   (LGLL)child->stats->confs,
		   (LGLL)child->stats->decisions);
	if (res == 10)
		lglpruneana(&state);
	lgldel(parent, state.map, parent->nvars * sizeof *state.map);
	lglrelease(child);
	return res;
}

static void
lglprune(LGL* lgl)
{
	int res, level = lgl->level - lgl->alevel;
	const char* reason;
	assert(!lgl->mt);
	assert(lgl->unassigned);
	assert(lgl->opts->prune.val);
	assert(level <= lgl->opts->prunelevel.val);
	lglstart(lgl, &lgl->times->prune);
	lgl->stats->prune.calls++;
	lgl->stats->prune.levels += level;
	lglprt(lgl,
		   2,
		   "[prune-%lld-%lld-%d] starting pruning after %lld decisions",
		   (LGLL)lgl->stats->prune.decs,
		   (LGLL)lgl->stats->prune.calls,
		   level,
		   lgl->stats->decisions);
	res = lglprunesat(lgl, level);
	if (res == 10) {
		reason = "satisfiable";
		lgl->stats->prune.sat++;
	} else if (res == 20) {
		reason = "unsatisfiable";
		lgl->stats->prune.uns++;
	} else {
		reason = "limit reached";
		lgl->stats->prune.lim++;
	}
	lglprt(lgl,
		   2,
		   "[prune-%lld-%lld-%d] pruning result '%s' (%s)",
		   (LGLL)lgl->stats->prune.decs,
		   (LGLL)lgl->stats->prune.calls,
		   level,
		   reason,
		   res == 10 ? "successful" : "unsuccessful");
	lglupdprunelim(lgl, res == 10);
	lglstop(lgl);
	lglrep(lgl, 2, 'P');
}

/*------------------------------------------------------------------------*/

static int
lglquatresolving(LGL* lgl)
{
	if (lgldelaying(lgl, "quatres", &lgl->limits->quatres.del.rem))
		return 0;
	if (lglwaiting(lgl, "quatres", lgl->opts->quatreswait.val))
		return 0;
	return lgl->opts->quatres.val;
}

static int
lglternresolving(LGL* lgl)
{
	if (lgldelaying(lgl, "ternres", &lgl->limits->ternres.del.rem))
		return 0;
	if (lglwaiting(lgl, "ternres", lgl->opts->ternreswait.val))
		return 0;
	return lgl->opts->ternres.val;
}

static int
lglgaussing(LGL* lgl)
{
	if (lgldelaying(lgl, "gauss", &lgl->limits->gauss.del.rem))
		return 0;
	if (lglwaiting(lgl, "gauss", lgl->opts->gausswait.val))
		return 0;
	if (!lglsmallirr(lgl))
		return 0;
	return lgl->opts->gauss.val;
}

static int
lglprobing(LGL* lgl)
{
	if (!lgl->opts->probe.val)
		return 0;
	if (lgl->opts->prbasic.val)
		return 1;
	if (!lglsmallirr(lgl))
		return 0;
	if (lgl->opts->prbsimple.val)
		return 1;
	if (lgl->opts->treelook.val)
		return 1;
	return 0;
}

static int
lglcarding(LGL* lgl)
{
	if (!lgl->opts->card.val)
		return 0;
	if (lgldelaying(lgl, "card", &lgl->limits->card.del.rem))
		return 0;
	if (lglwaiting(lgl, "card", lgl->opts->cardwait.val))
		return 0;
	return 1;
}

static int
lglcceing(LGL* lgl)
{
	if (!lgl->opts->cce.val)
		return 0;
	if (lgl->allfrozen)
		return 0;
	if (lgldelaying(lgl, "cce", &lgl->limits->cce.del.rem))
		return 0;
	if (lglwaiting(lgl, "cce", lgl->opts->ccewait.val))
		return 0;
	if (!lglsmallirr(lgl))
		return 0;
	return 1;
}

/*------------------------------------------------------------------------*/

static void
lglupdlocslim(LGL* lgl, int updconflimtoo)
{
	int rem = lglrem(lgl), varslimit, vardelta;
	vardelta = (rem * lgl->opts->locsvared.val + 999) / 1000;
	if (vardelta > rem)
		vardelta = rem;
	varslimit = rem - vardelta;
	lgl->limits->locs.vars = varslimit;
	lglprt(lgl,
		   1 + !lgl->opts->locs.val,
		   "[locs-lim] next local search variable limit %d = %d - %d",
		   varslimit,
		   rem,
		   vardelta);
	if (!updconflimtoo)
		return;
	lgl->limits->locs.inc += lgl->opts->locscint.val;
	assert(lgl->limits->locs.confs <= LLMAX - lgl->limits->locs.inc);
	lgl->limits->locs.confs = lgl->stats->confs + lgl->limits->locs.inc;
	lglprt(lgl,
		   1 + !lgl->opts->locs.val,
		   "[locs-lim] next local search conflict limit increased by %d to %lld",
		   lgl->limits->locs.inc,
		   lgl->limits->locs.confs);
}

static int
lglocsing(LGL* lgl)
{
#ifndef NLGLYALSAT
	int rem;
	if (!lgl->opts->locs.val)
		return 0;
	if (lgl->limits->locs.confs > lgl->stats->confs)
		return 0;
	if (lgl->stats->irr.clauses.cur > lgl->opts->locsclim.val) {
		lglprt(lgl, 2, "[locs] skipped due to too many clauses");
		return 0;
	}
	rem = lglrem(lgl);
	if (rem < lgl->limits->locs.vars) {
		lglprt(lgl, 2, "[locs-limit] local search skipped (remaining %d < limit %d)", rem, lgl->limits->locs.vars);
		lglupdlocslim(lgl, 1);
		return 0;
	}
	if (lglwaiting(lgl, "locs", lgl->opts->locswait.val))
		return 0;
	if (lgl->opts->locs.val < 0)
		return 1;
	return (lgl->stats->locs.count < lgl->opts->locs.val);
#else
	(void)lgl;
	return 0;
#endif
}

#ifndef NLGLYALSAT

static void
lglocscopy(LGL* lgl, Yals* yals)
{
	int idx, sign, lit, blit, tag, red, other, other2;
	int count, redcount, units, tmp;
	const int *p, *w, *eow, *c;
	HTS* hts;
	Stk* s;
	assert(!lgl->level);
	if (!lgl->mt && !lglbcp(lgl))
		lglmt(lgl);
	if (!lgl->mt)
		lglgc(lgl);
	if (lgl->mt)
		return;
	count = redcount = units = 0;
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			assert(!lglval(lgl, lit));
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				red = blit & REDCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (red && tag == BINCS && lgl->opts->locsred.val < 2)
					continue;
				if (red && tag == TRNCS && lgl->opts->locsred.val < 3)
					continue;
				if (tag != BINCS && tag != TRNCS)
					continue;
				other = blit >> RMSHFT;
				if (abs(other) < idx)
					continue;
				assert(!lglval(lgl, other));
				if (tag == TRNCS) {
					other2 = *p;
					if (abs(other2) < idx)
						continue;
					assert(!lglval(lgl, other2));
				} else
					other2 = 0;
				yals_add(yals, lit);
				yals_add(yals, other);
				if (other2)
					yals_add(yals, other2);
				yals_add(yals, 0);
				if (red)
					redcount++;
				count++;
			}
		}
	for (idx = 2; idx < lgl->nvars; idx++) {
		tmp = lglifixed(lgl, idx);
		if (!tmp)
			continue;
		lit = (tmp < 0) ? -idx : idx;
		yals_add(yals, lit);
		yals_add(yals, 0);
		units++;
	}
	for (red = -1; red < MAXGLUE; red++) {
		s = red < 0 ? &lgl->irr : lgl->red + red;
		if (red >= 0 && lgl->opts->locsred.val < 4)
			break;
		for (c = s->start; c < s->top; c = p + 1) {
			p = c;
			if (*p >= NOTALIT)
				continue;
			while ((other = *p)) {
				assert(!lglval(lgl, other));
				yals_add(yals, other);
				p++;
			}
			yals_add(yals, 0);
			if (red >= 0)
				redcount++;
			count++;
		}
	}

	lglprt(lgl, 1, "[locs-%d] copied %d irredundant clauses", lgl->stats->locs.count, count - redcount);
	if (redcount)
		lglprt(lgl, 1, "[locs-%d] copied %d redundant clauses", lgl->stats->locs.count, redcount);
	if (units)
		lglprt(lgl, 1, "[locs-%d] copied %d units", lgl->stats->locs.count, units);

	for (count = 0; count < (int)lglcntstk(&lgl->assume); count++) {
		lit = lglpeek(&lgl->assume, count);
		if (abs(lit) <= 0)
			continue;
		yals_add(yals, lit);
		yals_add(yals, 0);
	}
	if (count)
		lglprt(lgl, 1, "[locs-%d] copied %d assumptions as %d unit clauses", lgl->stats->locs.count, count, count);
	else
		lglprt(lgl, 2, "[locs-%d] no assumptions copied", lgl->stats->locs.count);
}

static int64_t
lglsetlocslim(LGL* lgl)
{
	int pen, boost;
	int64_t limit;
	if (lgl->opts->locsrtc.val) {
		limit = LLMAX;
		lglprt(lgl, 1, "[locs-%d] really no limit (run to completion)", lgl->stats->locs.count);
	} else {
		limit = (lgl->opts->locsreleff.val * lglvisearch(lgl)) / 100;
		if (limit < lgl->opts->locsmineff.val)
			limit = lgl->opts->locsmineff.val;
		if (lgl->opts->locsmaxeff.val >= 0 && limit > lgl->opts->locsmaxeff.val)
			limit = lgl->opts->locsmaxeff.val;
		if (lgl->stats->locs.count <= 1 && lgl->opts->boost.val && lgl->opts->locsboost.val > 1) {
			boost = lgl->opts->locsboost.val;
			lglprt(lgl, 1, "[locs-%d] intially boosting limit by factor of %d", lgl->stats->locs.count, boost);
		} else
			boost = 1;
		limit *= boost;
		pen = lglitszpen(lgl);
		limit >>= pen;
		lglprt(lgl, 1, "[locs-%d] limit %lld literal penalty %d", lgl->stats->locs.count, (LGLL)limit, pen);
	}
	return limit;
}

#endif

static int
lglocsaux(LGL* lgl, int hitlim)
{
	int lkhd = 0;
#ifndef NLGLYALSAT
	int save = lgl->opts->locsexport.val && lgl->opts->phasesave.val;
	int res, min, pos, neg, idx, lit, old_val, len;
	const int set = lgl->opts->locset.val;
	unsigned long long seed;
	char* prefix;
	int64_t limit;
	AVar* av;
	Val val;
	Yals* yals;
	lglstart(lgl, &lgl->times->locs);
	if (lgl->level)
		lglbacktrack(lgl, 0);
	if (!lgl->stats->locs.count++) {
		const char* yals_version();
		if (lgl->opts->verbose.val > 0)
			lglprt(lgl, 1, "[locs-%d] YalSAT Version %s", lgl->stats->locs.count, yals_version());
		else if (lgl->opts->locsbanner.val) {
			char* prefix = lgl->prefix;
			lgl->prefix = "c ";
			lglprt(lgl, 0, "YalSAT Version %s", yals_version());
			lgl->prefix = prefix;
		}
	}
	yals = yals_new_with_mem_mgr(lgl, (YalsMalloc)lglnew, (YalsRealloc)lglrsz, (YalsFree)lgldel);
	yals_setout(yals, lgl->out);
	yals_setopt(yals, "verbose", 0);
	len = strlen(lgl->prefix) + 80;
	NEW(prefix, len);
	if (lgl->tid >= 0)
		sprintf(prefix, "%s%d [locs-%d] ", lgl->prefix, lgl->tid, lgl->stats->locs.count);
	else
		sprintf(prefix, "%s[locs-%d] ", lgl->prefix, lgl->stats->locs.count);
	yals_setprefix(yals, prefix);
	DEL(prefix, len);
	old_val = yals_getopt(yals, "verbose");
	if (old_val < lgl->opts->verbose.val)
		yals_setopt(yals, "verbose", lgl->opts->verbose.val);
	seed = lglrand(lgl);
	if (lgl->tid >= 0)
		seed *= lgl->tid + 1;
	seed += 1237 * (unsigned)lgl->stats->locs.count;
	yals_srand(yals, seed);
	if (hitlim > 0)
		yals_setopt(yals, "hitlim", hitlim);
	lglocscopy(lgl, yals);
	if (lgl->mt)
		goto DONE;
	limit = lglsetlocslim(lgl);
	pos = neg = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		av = lglavar(lgl, idx);
		if (set == 2 && av->phase > 0)
			lit = idx;
		else if (set == 2 && av->phase < 0)
			lit = -idx;
		else if (set == 1 && av->locsval > 0)
			lit = idx;
		else if (set == 1 && av->locsval < 0)
			lit = -idx;
		else
			continue;
		assert(lit);
		if (lit < 0)
			neg++;
		else
			pos++;
		yals_setphase(yals, lit);
	}
	lglprt(lgl,
		   1,
		   "[locs-%d] importing %d positive %.0f%% and %d negative %.0f%% phases",
		   lgl->stats->locs.count,
		   pos,
		   lglpcnt(pos, pos + neg),
		   neg,
		   lglpcnt(neg, pos + neg));
	if (limit < LLMAX / 1000)
		limit *= 1000;
	else
		limit = LLMAX;
	yals_setmemslimit(yals, limit);
	if (lgl->cbs && lgl->cbs->term.fun)
		yals_seterm(yals, lgl->cbs->term.fun, lgl->cbs->term.state);
	if (lgl->cbs && lgl->cbs->getime)
		yals_setime(yals, lgl->cbs->getime);
	if (lgl->cbs && lgl->cbs->msglock.lock)
		yals_setmsglock(yals, lgl->cbs->msglock.lock, lgl->cbs->msglock.unlock, lgl->cbs->msglock.state);
	res = yals_sat(yals);
	min = yals_minimum(yals);
	lglprt(lgl, 1, "[locs-%d] local search returns %d with minimum %d", lgl->stats->locs.count, res, min);
	if (min < lgl->stats->locs.min)
		lgl->stats->locs.min = min;
	lgl->stats->locs.flips += yals_flips(yals);
	lgl->stats->locs.mems += yals_mems(yals);
	if (res == 10)
		save = 1;
	pos = neg = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		val = yals_deref(yals, idx);
		assert(abs(val) == 1);
		av = lglavar(lgl, idx);
		if (val < 0)
			neg++, av->locsval = -1;
		else if (val > 0)
			pos++, av->locsval = 1;
		if (save)
			av->phase = val;
	}
	lglprt(lgl,
		   1,
		   "[locs-%d] %s %d positive %.0f%% and %d negative %.0f%% phases",
		   lgl->stats->locs.count,
		   save ? "exported" : "found",
		   pos,
		   lglpcnt(pos, pos + neg),
		   neg,
		   lglpcnt(neg, pos + neg));
DONE:
	if (lgl->opts->verbose.val >= 1)
		yals_stats(yals);
	yals_del(yals);
	lglstop(lgl);
#else
	(void)lgl, (void)hitlim;
#endif
	return lkhd;
}

static void
lglocs(LGL* lgl)
{
	(void)lglocsaux(lgl, 0);
	lglupdlocslim(lgl, 1);
}

static int
lglocslook(LGL* lgl)
{
	int res = lglocsaux(lgl, 100000), elit;
	Ext* ext;
	if (res) {
		elit = lglexport(lgl, res);
		ext = lglelit2ext(lgl, elit);
		if (!ext->eliminated && !ext->blocking) {
			lglprt(lgl, 1, "[locslook] best local search look-ahead %d", res);
			if (ext->melted) {
				ext->melted = 0;
				LOG(2, "look-ahead winner external %d not melted anymore", elit);
			} else
				LOG(2, "look-ahead winner external %d was not melted anyhow", elit);
		} else {
			lglprt(lgl, 1, "[locslook] no valid local search look-ahead");
			lglprt(lgl, 1, "[locslook] falling back to JWH");
			res = lgljwhlook(lgl);
		}
	} else
		LOG(1, "no proper local search look-ahead literal found");

	return res;
}

/*------------------------------------------------------------------------*/

static int
lglforklit(int ilit)
{
	int idx = abs(ilit), res;
	assert(idx > 1);
	res = idx - 1;
	if (ilit < 0)
		res = -res;
	return res;
}

/*------------------------------------------------------------------------*/

static int
lglsweeping(LGL* lgl)
{
	if (!lglsmallirr(lgl))
		return 0;
	if (!lgl->opts->sweep.val)
		return 0;
	if (lgldelaying(lgl, "sweep", &lgl->limits->sweep.del.rem))
		return 0;
	if (lglwaiting(lgl, "sweep", lgl->opts->sweepwait.val))
		return 0;
	return 1;
}

static void
lglinitsweep(LGL* lgl)
{
	int idx, lit, pos, neg, rem = 0, ret = 0;
	AVar* av;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		if (lglavar(lgl, idx)->donotsweep)
			ret++;
		else
			rem++;
	}
	if (!rem) {
		ret = 0;
		for (idx = 2; idx < lgl->nvars; idx++) {
			if (!lglisfree(lgl, idx))
				continue;
			lglavar(lgl, idx)->donotsweep = 0;
			rem++;
		}
	}
	pos = neg = 0;
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		av = lglavar(lgl, idx);
		if (av->donotsweep)
			continue;
		if (av->phase <= 0)
			lit = -idx, neg++;
		else
			lit = idx, pos++;
		lglpushstk(lgl, &lgl->swp->partition, lit);
	}
	assert(pos + neg == rem);
	if (rem > 1) {
		lglpushstk(lgl, &lgl->swp->partition, 0);
		lgl->swp->partitions++;
	} else if (rem) {
		assert(rem == 1);
		lglclnstk(&lgl->swp->partition);
	} else
		assert(lglmtstk(&lgl->swp->partition));
	lglprt(lgl,
		   1,
		   "[sweep-%d-%d] %d pos %.1f%% + %d neg %.1f%% = %d sched %.1f%% ret %d",
		   lgl->stats->sweep.count,
		   lgl->swp->round,
		   pos,
		   lglpcnt(pos, rem),
		   neg,
		   lglpcnt(neg, rem),
		   rem,
		   lglpcnt(rem, lgl->nvars - 2),
		   ret);
}

static double
lglsweepavgpartitionsize(LGL* lgl)
{
	int count, n;
	double res;
	assert(lgl->swp);
	if (lglmtstk(&lgl->swp->partition))
		return 0;
	count = lglcntstk(&lgl->swp->partition);
	n = lgl->swp->partitions;
	assert(0 < n), assert(n < count);
	res = count - n;
	res /= n;
	return res;
}

static void
lgladdsweepbincls(LGL* lgl, int a, int b)
{
	assert(lgl->swp);
	assert(lglmtstk(&lgl->clause));
	lglpushstk(lgl, &lgl->clause, a);
	lglpushstk(lgl, &lgl->clause, b);
	lglpushstk(lgl, &lgl->clause, 0);
	LOG(2, "sweeping detected binary clause", a, b);
	lgldrupligaddcls(lgl, REDCS);
	lgladdcls(lgl, REDCS, 0, 1);
	lglclnstk(&lgl->clause);
}

static void
lglsweepsatinitlit(LGL* lgl, int lit, int a, int b)
{
	int idx = abs(lit), decision;
	AVar* av;
	assert(a > 0), assert(b > 0);
	if (a == idx)
		return;
	if (b == idx)
		return;
	av = lglavar(lgl, idx);
	if (av->mark)
		return;
	av->mark = 1;
	if (lgldefphase(lgl, idx) <= 0)
		decision = -idx;
	else
		decision = idx;
	LOG(2, "sweep SAT adding decision %d", decision);
	lglpushstk(lgl, &lgl->swp->decision.stk, decision);
}

#define LGLCMPSWPDEC(A, B) lgldcmp(lgl, *(B), *(A))

static void
lglsweepsatinit(LGL* lgl, int a, int b)
{
	int i, idx, sign, lit, blit, tag, red, other, other2, lidx, old, count;
	const int sweepred = lgl->opts->sweepred.val;
	const int sweepirr = lgl->opts->sweepirr.val;
	const int *w, *eow, *p, *c, *l;
	HTS* hts;
	a = abs(a), b = abs(b);
	if (b > a)
		SWAP(int, a, b);
	if (a == lgl->swp->decision.cached.a && b == lgl->swp->decision.cached.b) {
		LOG(2, "sweep SAT using cached environment of %d and %d", a, b);
		lgl->stats->sweep.cached++;
		count = lglcntstk(&lgl->swp->decision.stk);
		goto DONE;
	}
	lglclnstk(&lgl->swp->decision.stk);
	for (i = 0; i <= 1; i++) {
		idx = i ? b : a;
		old = lglcntstk(&lgl->swp->decision.stk);
		LOG(2, "sweep SAT initializing environment of %d", idx);
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				red = blit & REDCS;
				if (tag == BINCS) {
					if (!red && sweepirr < 1)
						continue;
					if (red && sweepred < 1)
						continue;
					other = blit >> RMSHFT;
					lglsweepsatinitlit(lgl, other, a, b);
				} else if (tag == TRNCS) {
					if (!red && sweepirr < 2)
						continue;
					if (red && sweepred < 2)
						continue;
					other = blit >> RMSHFT;
					lglsweepsatinitlit(lgl, other, a, b);
					other2 = *p;
					lglsweepsatinitlit(lgl, other2, a, b);
				} else {
					assert(tag == LRGCS);
					if (!red && sweepirr < 3)
						continue;
					if (red && sweepred < 3)
						continue;
					lidx = *p;
					c = lglidx2lits(lgl, red, lidx);
					for (l = c; (other = *l); l++)
						lglsweepsatinitlit(lgl, other, a, b);
				}
			}
		}
		count = lglcntstk(&lgl->swp->decision.stk) - old;
		LOG(2, "sweep SAT environment of %d contains %d decisions", idx, count);
		(void)count;
	}
	for (p = lgl->swp->decision.stk.start; p < lgl->swp->decision.stk.top; p++)
		lglavar(lgl, *p)->mark = 0;
	lgl->swp->decision.cached.a = a;
	lgl->swp->decision.cached.b = b;
	count = lglcntstk(&lgl->swp->decision.stk);
	SORT(int, lgl->swp->decision.stk.start, count, LGLCMPSWPDEC);
DONE:
	LOG(2, "sweep SAT environment of %d and %d, contains %d decisions", a, b, count);
	lgl->swp->sumsize.envs += count;
	lgl->stats->sweep.sumsize.envs += count;
}

static int
lglsweepdec(LGL* lgl)
{
	int i, lit = 0;
	for (i = 0; i < (int)lglcntstk(&lgl->swp->decision.stk); i++) {
		lit = lglpeek(&lgl->swp->decision.stk, i);
		if (!lglval(lgl, lit))
			break;
	}
	if (i == (int)lglcntstk(&lgl->swp->decision.stk)) {
		LOG(2, "no sweep implication decision left");
		return 0;
	}
	LOG(2,
		"sweep typed %d implication check decision %d score %s",
		lgl->swp->type,
		lit,
		lglscr2str(lgl, lglqvar(lgl, lit)->score));
	lgliassume(lgl, lit);
	lgl->swp->decision.count++;
	lgl->stats->sweep.decs++;
	return 1;
}

static int
lglsweepsat(LGL* lgl, int lim, int a, int b)
{
	int n = 0, aval, bval;
	assert(lgl->level == 2);
	lgl->stats->sweep.sat++;
	lgl->swp->sat++;
	LOG(2, "deep sweep implication SAT check %d/%d decision limit %d", lgl->swp->sat, lgl->stats->sweep.sat, lim);
	for (;;) {
		if (lglbcp(lgl)) {
			if (lgl->mt)
				return 0;
			if ((aval = lglval(lgl, a)) < 0)
				return 0;
			if ((bval = lglval(lgl, b)) > 0)
				return 0;
			if (!aval) {
				assert(!lgl->level);
				lgliassume(lgl, a);
				lgl->swp->decision.count++;
				lgl->stats->sweep.decs++;
				continue;
			}
			LOG(2, "sweep typed %d assumption %d score %s", lgl->swp->type, a, lglscr2str(lgl, lglqvar(lgl, a)->score));
			if (!bval) {
				assert(lgl->level <= 1);
				lgliassume(lgl, -b);
				lgl->swp->decision.count++;
				lgl->stats->sweep.decs++;
				continue;
			}
			LOG(2, "sweep typed %d assumption %d score %s", lgl->swp->type, b, lglscr2str(lgl, lglqvar(lgl, b)->score));
			assert(lglval(lgl, a) > 0), assert(lglval(lgl, b) < 0);
			if (++n > lim) {
				LOG(2, "deep sweep SAT checking decision limit %d reached", lim);
				return 1;
			}
			if (!lglsweepdec(lgl))
				return 1;
		} else if (!lglana(lgl))
			return 0;
	}
}

static int
lglsweepliesaux(LGL* lgl, int a, int b)
{
	int ok, val, res, dec;
	assert(!lgl->level);
	if (lgl->mt)
		return 0;
	if (lglifixed(lgl, a))
		return 0;
	if (lglifixed(lgl, b))
		return 0;
	lgliassume(lgl, a);
	ok = lglbcp(lgl);
	if (ok) {
		assert(lgl->level == 1);
		val = lglval(lgl, b);
		if (val > 0) {
			lglbacktrack(lgl, 0);
			LOG(1, "sweep implication check %d -> %d subsumed by BCP", a, b);
			res = 1;
		} else if (val < 0) {
			LOG(1, "sweep implication check %d -> %d incompatible with BCP", a, b);
			res = 0;
		} else {
			lgliassume(lgl, -b);
			ok = lglbcp(lgl);
			if (ok) {
				dec = lgl->stats->sweep.count - 1;
				dec *= lgl->opts->sweepfacdec.val;
				if (dec < lgl->opts->sweepmindec.val)
					dec = lgl->opts->sweepmindec.val;
				if (dec > lgl->opts->sweepmaxdec.val)
					dec = lgl->opts->sweepmaxdec.val;
				if (dec) {
					lglsweepsatinit(lgl, a, b);
					ok = lglsweepsat(lgl, dec, a, b);
					if (ok) {
						LOG(1, "deep sweep implication check %d -> %d failed", a, b);
						res = 0;
					} else {
						lgl->stats->sweep.unsat++;
						LOG(1, "deep sweep implication check %d -> %d succeeded", a, b);
						if (lgl->level)
							lglbacktrack(lgl, 0);
						res = 1;
					}
				} else {
					LOG(2, "skipping sweep implication SAT check");
					res = 0;
				}
			} else {
				LOG(1, "shallow sweep implication check %d -> %d succeeded", a, b);
				lglbacktrack(lgl, 0);
				lgladdsweepbincls(lgl, -a, b);
				lgl->stats->sweep.impls++;
				res = 1;
			}
		}
	} else {
		lglbacktrack(lgl, 0);
		LOG(1, "failed literal %d during sweep implication check", a);
		lgl->stats->sweep.failed++;
		lglunit(lgl, -a);
		if (!lglbcp(lgl)) {
			LOG(1, "empty clause propagating unit in sweep implication check");
			lglmt(lgl);
		}
		res = 0;
	}
	return res;
}

static int
lglsweeplies(LGL* lgl, int a, int b)
{
	int res = lglsweepliesaux(lgl, a, b);
	if (!res)
		lgl->stats->sweep.queries.type[lgl->swp->type].sat++;
	else
		lgl->stats->sweep.queries.type[lgl->swp->type].unsat++;
	lglprt(lgl,
		   2,
		   "[sweep-%d-%d-%d] implication check %s",
		   lgl->stats->sweep.count,
		   lgl->swp->round,
		   lgl->swp->query,
		   res ? "succeeded" : "failed");
	return res;
}

static void
lglsweepquery(LGL* lgl)
{
	int count = lglcntstk(&lgl->swp->partition), l, r, size;
	int p, q, a, b, i, c, newpartitions, newsize, newtotal;
	int total = count - lgl->swp->partitions, remove, val;
	int* partition = lgl->swp->partition.start;
	int minsize, best, start, bestscore;
	Stk newpartition;
	switch (lgl->swp->query % 5) {
		case 1:
			lgl->swp->type = 2;
			break; // score
		case 2:
			lgl->swp->type = 1;
			break; // size
		case 3:
			lgl->swp->type = 2;
			break; // score
		case 4:
			lgl->swp->type = 1;
			break; // size
		case 0:
			lgl->swp->type = 0;
			break; // random
	}
	lgl->stats->sweep.queries.total++;
	lgl->stats->sweep.queries.type[lgl->swp->type].count++;
	assert(count > 0);
	lglprt(lgl,
		   2,
		   "[sweep-%d-%d-%d] %d classes with %d vars of avg size %.1f",
		   lgl->stats->sweep.count,
		   lgl->swp->round,
		   lgl->swp->query,
		   lgl->swp->partitions,
		   total,
		   lglsweepavgpartitionsize(lgl));

	l = lglrand(lgl) % count;
	if (!partition[l]) {
		assert(l > 0);
		l--;
	}
	while (l > 0 && partition[l - 1])
		l--;
	if (lgl->swp->type == 1) { // min size partition
		INCSTEPS(sweep.steps);
		best = start = l;
		minsize = INT_MAX;
		do {
			assert(l < count);
			INCSTEPS(sweep.steps);
			size = 0;
			for (r = l; (a = partition[r]); r++) {
				COVER(lglifixed(lgl, a));
				if (lglifixed(lgl, a))
					continue;
				size++;
			}
			if (size < minsize)
				best = l, minsize = size;
			if ((l = r + 1) == count)
				l = 0;
		} while (l != start);
		l = best;
	} else if (lgl->swp->type == 2) { // best score partition
		INCSTEPS(sweep.steps);
		best = start = l;
		bestscore = 0;
		do {
			assert(l < count);
			INCSTEPS(sweep.steps);
			for (r = l; (a = partition[r]); r++) {
				if (lglifixed(lgl, a))
					continue;
				if (bestscore && lgldcmp(lgl, a, bestscore) < 0)
					continue;
				best = l, bestscore = a;
			}
			if ((l = r + 1) == count)
				l = 0;
		} while (l != start);
		l = best;
	} else
		assert(lgl->swp->type == 0); // random partition
	INCSTEPS(sweep.steps);
	for (r = l; partition[r]; r++)
		;
	size = r - l;
	assert(1 < size), assert(size < count);
	lgl->swp->sumsize.classes += size;
	lgl->stats->sweep.sumsize.classes += size;
	lglprt(lgl,
		   2,
		   "[sweep-%d-%d-%d] "
		   "type %d picked size %d class [%d:%d] %.1f%%",
		   lgl->stats->sweep.count,
		   lgl->swp->round,
		   lgl->swp->query,
		   lgl->swp->type,
		   size,
		   l,
		   r - 1,
		   lglpcnt(size, total));
	assert(size >= 2);
	p = lglrand(lgl) % size;
	q = lglrand(lgl) % size;
	if (p == q)
		q = !p;
	if (p + q >= size)
		q = p + q - size;
	p += l, assert(l <= p), assert(p < r);
	q += l, assert(l <= p), assert(q < r);
	assert(p != q);
	a = partition[p];
	b = partition[q];
	for (i = l; i < r; i++) {
		if (i == p)
			continue;
		if (i == q)
			continue;
		c = partition[i];
		if (lgldcmp(lgl, c, a) > 0)
			a = c, p = i;
	}
	for (i = l; i < r; i++) {
		if (i == p)
			continue;
		if (i == q)
			continue;
		c = partition[i];
		if (lgldcmp(lgl, c, b) > 0)
			b = c, q = i;
	}
	assert(a), assert(b);
	assert(abs(a) != abs(b));
	LOG(2, "picked literal pair %d and %d at position %d and %d", a, b, p, q);
	assert(!lgl->level);
	if (lglsweeplies(lgl, a, b) && lglsweeplies(lgl, b, a)) {
		assert(!lgl->level);
		lgl->stats->sweep.equivs++;
		remove = b;
		LOG(2, "found equivalence between %d and %d and will remove %d", a, b, b);
	} else
		remove = 0;
	CLR(newpartition);
	newsize = newpartitions = 0;
	if (!lgl->mt) {
		for (val = -1; val <= 1; val++) {
			for (i = 0; i < count; i++) {
				if ((c = partition[i])) {
					if (c == remove)
						continue;
					if (lglifixed(lgl, c))
						continue;
					if (lglval(lgl, c) != val)
						continue;
					lglpushstk(lgl, &newpartition, c);
					newsize++;
				} else if (newsize > 1) {
					lglpushstk(lgl, &newpartition, 0);
					newpartitions++;
					newsize = 0;
				} else if (newsize) {
					assert(newsize == 1);
					(void)lglpopstk(&newpartition);
					newsize = 0;
				}
			}
		}
		ADDSTEPS(sweep.steps, (count >> 3));
	}
	assert(!newsize);
	assert(!newpartitions || !newpartition.top[-1]);
	newtotal = lglcntstk(&newpartition) - newpartitions;
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	if (lgl->swp->partitions >= newpartitions)
		lglprt(lgl,
			   2,
			   "[sweep-%d-%d-%d] removed %d literals and removed %d classes",
			   lgl->stats->sweep.count,
			   lgl->swp->round,
			   lgl->swp->query,
			   total - newtotal,
			   lgl->swp->partitions - newpartitions);
	else
		lglprt(lgl,
			   2,
			   "[sweep-%d-%d-%d] removed %d literals and added %d classes",
			   lgl->stats->sweep.count,
			   lgl->swp->round,
			   lgl->swp->query,
			   total - newtotal,
			   newpartitions - lgl->swp->partitions);
	lgl->swp->partitions = newpartitions;
	lglfitstk(lgl, &newpartition);
	lglrelstk(lgl, &lgl->swp->partition);
	lgl->swp->partition = newpartition;
}

static void
lglsetsweeplim(LGL* lgl)
{
	int count = lgl->stats->sweep.count - lgl->opts->sweepboostdel.val;
	int64_t limit, irrlim;
	int pen, szpen;
	if (lgl->opts->sweeprtc.val > 1) {
		lgl->limits->sweep.steps = LLMAX;
		lglprt(lgl, 1, "[sweep-%d] really no limit (run to completion)", lgl->stats->sweep.count);
	} else if (lgl->opts->sweeprtc.val || lgl->opts->prbrtc.val ||
			   (count > 0 && lglrem(lgl) < lgl->opts->sweeprtcintvlim.val && !(count % lgl->opts->sweeprtcint.val))) {
		limit = 4000000000ll;
		lgl->limits->sweep.steps = lgl->stats->sweep.steps + limit;
		lglprt(lgl, 1, "[sweep-%d] almost no limit of %lld steps", lgl->stats->sweep.count, (LGLL)limit);
	} else {
		limit = (lgl->opts->sweepreleff.val * lglvisearch(lgl)) / 1000;
		if (limit < lgl->opts->sweepmineff.val)
			limit = lgl->opts->sweepmineff.val;
		if (lgl->opts->sweepmaxeff.val >= 0 && limit > lgl->opts->sweepmaxeff.val)
			limit = lgl->opts->sweepmaxeff.val;
		if (count > 0 && (count <= 1 || !(count % lgl->opts->sweepboostint.val)) &&
			lglrem(lgl) < lgl->opts->sweepboostvlim.val && lgl->opts->boost.val && lgl->opts->sweepboost.val > 1) {
			lglprt(
				lgl, 1, "[sweep-%d] boosting sweeping limit by %d", lgl->stats->sweep.count, lgl->opts->sweepboost.val);
			limit *= lgl->opts->sweepboost.val;
		}
		limit >>= (pen = lgl->limits->sweep.pen + (szpen = lglszpen(lgl)));
		irrlim = (2 * lgl->stats->irr.clauses.cur) >> szpen;
		if (lgl->opts->irrlim.val && limit < irrlim) {
			limit = irrlim;
			lglprt(lgl,
				   1,
				   "[sweep-%d] limit %lld based on %d irredundant clauses penalty %d",
				   lgl->stats->sweep.count,
				   (LGLL)limit,
				   lgl->stats->irr.clauses.cur,
				   szpen);
		} else
			lglprt(lgl,
				   1,
				   "[sweep-%d] limit %lld penalty %d = %d + %d",
				   lgl->stats->sweep.count,
				   (LGLL)limit,
				   pen,
				   lgl->limits->sweep.pen,
				   szpen);
		lgl->limits->sweep.steps = lgl->stats->sweep.steps + limit;
	}
}

static void
lglsweepretain(LGL* lgl)
{
	int idx, ret = 0, rem = 0, sum;
	const int* p;
	for (idx = 2; idx < lgl->nvars; idx++)
		lglavar(lgl, idx)->donotsweep = 1;
	for (p = lgl->swp->partition.start; p < lgl->swp->partition.top; p++) {
		if (!(idx = *p))
			continue;
		lglavar(lgl, idx)->donotsweep = 0;
	}
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		if (lglavar(lgl, idx)->donotsweep)
			ret++;
		else
			rem++;
	}
	if (rem) {
		sum = rem + ret;
		lglprt(lgl,
			   1,
			   "[sweep-%d-%d] %d variables remain %.0f%% (%d retained %.0f%%)",
			   lgl->stats->sweep.count,
			   lgl->swp->round,
			   rem,
			   lglpcnt(rem, sum),
			   ret,
			   lglpcnt(ret, sum));
	} else {
		lglprt(lgl, 1, "[sweep-%d-%d] fully completed sweeping round", lgl->stats->sweep.count, lgl->swp->round);
		for (idx = 2; idx < lgl->nvars; idx++)
			lglavar(lgl, idx)->donotsweep = 0;
		lgl->sweeprtc = 1;
	}
}

static int
lglsweep(LGL* lgl)
{
	int deltarem, deltafailed, deltaimpls, deltaequivs, deltaorigrem;
	int round, origrem, oldrem, oldfailed, oldimpls, oldequivs;
	int64_t oldsteps, deltasteps, origqueries, deltaqueries;
	int success, forgive;
	lglstart(lgl, &lgl->times->sweep);
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	assert(!lgl->simp), assert(!lgl->sweeping);
	lgl->simp = lgl->sweeping = 1;
	if (!lglbcp(lgl))
		goto DONE;
	lglgc(lgl);
	if (lgl->mt)
		goto DONE;
	lgl->stats->sweep.count++;
	lglsetsweeplim(lgl);
	origrem = lglrem(lgl);
	origqueries = lgl->stats->sweep.queries.total;
	round = 0;
	forgive = lgl->opts->sweepforgive.val;
RESTART:
	round++;
	lgl->stats->sweep.rounds++;
	oldrem = lglrem(lgl);
	oldfailed = lgl->stats->sweep.failed;
	oldimpls = lgl->stats->sweep.impls;
	oldequivs = lgl->stats->sweep.equivs;
	oldsteps = lgl->stats->sweep.steps;
	assert(!lgl->swp);
	NEW(lgl->swp, 1);
	lgl->swp->round = round;
	lglinitsweep(lgl);
	for (lgl->swp->query = 1;
		 !lgl->mt && !lglmtstk(&lgl->swp->partition) && lgl->stats->sweep.steps <= lgl->limits->sweep.steps;
		 lgl->swp->query++)
		lglsweepquery(lgl);
	deltasteps = lgl->stats->sweep.steps - oldsteps;
	lglprt(lgl,
		   1,
		   "[sweep-%d-%d] %lld steps %d queries %.2f avg class size",
		   lgl->stats->sweep.count,
		   round,
		   (LGLL)deltasteps,
		   lgl->swp->query,
		   lglavg(lgl->swp->sumsize.classes, lgl->swp->query));
	lglprt(lgl,
		   1,
		   "[sweep-%d-%d] %d decisions %d SAT calls %.2f avg env size",
		   lgl->stats->sweep.count,
		   round,
		   lgl->swp->decision.count,
		   lgl->swp->sat,
		   lglavg(lgl->swp->sumsize.envs, lgl->swp->sat));
	lglrelstk(lgl, &lgl->swp->decision.stk);
	lglsweepretain(lgl);
	lglrelstk(lgl, &lgl->swp->partition);
	if (!lgl->mt)
		lgldecomp(lgl);
	assert(lgl->swp->round == round);
	DEL(lgl->swp, 1);
	lgl->swp = 0;
	deltarem = oldrem - lglrem(lgl);
	deltafailed = lgl->stats->sweep.failed - oldfailed;
	deltaimpls = lgl->stats->sweep.impls - oldimpls;
	deltaequivs = lgl->stats->sweep.equivs - oldequivs;
	lglprt(lgl,
		   1,
		   "[sweep-%d-%d] removed %d vars (%d failed, %d eqs, %d impls)",
		   lgl->stats->sweep.count,
		   round,
		   deltarem,
		   deltafailed,
		   deltaequivs,
		   deltaimpls);
	if ((deltarem || forgive > 0) && !lgl->mt &&
		(lgl->opts->sweepmaxround.val < 0 || round < lgl->opts->sweepmaxround.val) &&
		lgl->stats->sweep.steps <= lgl->limits->sweep.steps) {
		if (deltarem)
			forgive = lgl->opts->sweepforgive.val;
		else
			forgive--;
		goto RESTART;
	}
	deltaorigrem = origrem - lglrem(lgl);
	if (!lgl->sweeprtc && lgl->stats->sweep.count <= lgl->opts->sweepsuccessmaxwortc.val) {
		success = 1;
		lglprt(lgl, 1, "[sweep-%d] considered successful since not run to completion yet", lgl->stats->sweep.count);
	} else if (deltaorigrem) {
		success = (origrem / lgl->opts->sweepsuccessrat.val <= deltaorigrem);
		if (!success)
			lglprt(lgl,
				   1,
				   "[sweep-%d] %d < 1/%d * %d = %d considered unsuccessful",
				   lgl->stats->sweep.count,
				   deltaorigrem,
				   lgl->opts->sweepsuccessrat.val,
				   origrem,
				   origrem / lgl->opts->sweepsuccessrat.val);
	} else
		success = 0;
	LGLUPDPEN(sweep, success);
	deltaqueries = lgl->stats->sweep.queries.total - origqueries;
	lglprt(lgl,
		   1,
		   "[sweep-%d] removed %d vars in TOTAL (%d rounds, %lld queries)",
		   lgl->stats->sweep.count,
		   deltaorigrem,
		   round,
		   (LGLL)deltaqueries);
DONE:
	assert(lgl->simp), assert(lgl->sweeping);
	lgl->simp = lgl->sweeping = 0;
	lglstop(lgl);
	return !lgl->mt;
}

/*------------------------------------------------------------------------*/

static int
lglqcmp(LGL* lgl, int a, int b)
{
	Flt ascore, bscore, pos, neg;
	QVar* p = lglqvar(lgl, a);
	QVar* q = lglqvar(lgl, b);
	if (!p->important && q->important)
		return -1;
	if (p->important && !q->important)
		return 1;
	pos = lgl->jwh[lglulit(a)];
	neg = lgl->jwh[lglulit(-a)];
	ascore = lglmulflt(pos, neg);
	pos = lgl->jwh[lglulit(b)];
	neg = lgl->jwh[lglulit(-b)];
	bscore = lglmulflt(pos, neg);
	if (ascore < bscore)
		return -1;
	if (ascore > bscore)
		return 1;
	return a - b;
}

#define LGLQCMP(A, B) lglqcmp(lgl, *(A), *(B))

static void
lglqueuesort(LGL* lgl)
{
	int count, i, lit;
	QVar* qv;
	lglstart(lgl, &lgl->times->queuesort);
	if (lgl->queue.mt)
		lglqueueflush(lgl);
	lgl->stats->queue.sorted++;
	assert(!lgl->queue.mt);
	count = lglcntstk(&lgl->queue.stk);
	assert(count > 0);
	SORT(int, lgl->queue.stk.start, count, LGLQCMP);
	for (i = 0; i < count; i++) {
		lit = lglpeek(&lgl->queue.stk, i);
		qv = lglqvar(lgl, lit);
		assert(qv->enqueued);
		assert(qv->pos >= 0);
		qv->pos = i;
	}
	lglprt(lgl,
		   2,
		   "[sort-queue-%d] sorted %d after %lld conflicts and %lld flushed queues",
		   (LGLL)lgl->stats->queue.sorted,
		   count,
		   (LGLL)lgl->stats->confs,
		   (LGLL)lgl->stats->queue.flushed);
	lgl->queue.next = count - 1;
	lglstop(lgl);
}

static int
lglqueuesorting(LGL* lgl)
{
	int count = lglcntstk(&lgl->queue.stk);
	assert(lgl->queue.mt <= count);
	if (lgl->queue.mt >= count)
		return 0;
	return lgl->opts->queuesort.val;
}

/*------------------------------------------------------------------------*/

#define LGLCMPSCR(A, B) lglscrcmp(lgl, *(A), *(B))

static void
lglshowscoredist(LGL* lgl)
{
	int idx, pos = 0, zero = 0, min = 0, sum = 0, diff;
	Flt* scores;
	QVar* q;
	lglstart(lgl, &lgl->times->showscoredist);
	NEW(scores, lgl->nvars);
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (!lglisfree(lgl, idx))
			continue;
		q = lglqvar(lgl, idx);
		if (q->score == FLTMIN)
			zero++;
		else if (q->score <= lgl->minscore)
			min++;
		else
			pos++;
		scores[sum++] = q->score;
	}
	lglprt(lgl,
		   1,
		   "[score-distribution-%d] "
		   "%d pos %.2f%%, %d min %.2f%%, %d zero %.2f%%",
		   lgl->stats->simp.count,
		   pos,
		   lglpcnt(pos, sum),
		   min,
		   lglpcnt(min, sum),
		   zero,
		   lglpcnt(zero, sum));
	SORT(Flt, scores, sum, LGLCMPSCR);
	diff = 0;
	for (idx = 0; idx < sum; idx++)
		if (!idx || lglscrcmp(lgl, scores[idx - 1], scores[idx]))
			diff++;
	DEL(scores, lgl->nvars);
	lglprt(lgl,
		   1,
		   "[score-distribution-%d] "
		   "%d different %.2f%% out of total %d variables",
		   lgl->stats->simp.count,
		   diff,
		   lglpcnt(diff, sum),
		   sum);
	lglstop(lgl);
}

static Features
lglfeatures(LGL* lgl)
{
	int idx, sign, lit, blit, tag, red, other, other1, len, lrg;
	const int *p, *w, *eow, *c;
	HTS* hts;
	Features res;
	CLR(res);
	res.n = lgl->stats->features;
	res.s = lglsec(lgl);
	res.vo = lgl->limits->inc.vars.start;
	res.vc = lglrem(lgl);
	res.co = lgl->limits->inc.clauses.start;
	res.cc = lgl->stats->irr.clauses.cur;
	res.x = lgl->stats->gauss.extracted.last;
	res.a1 = lgl->stats->card.found.am1.last.cnt;
	res.a2 = lgl->stats->card.found.am2.last.cnt;
	res.c = lgl->stats->confs / 1000;
	res.o = lgl->stats->otfs.total / 1000;
	res.g = lgl->stats->glue.slow.val >> 32;
	res.j = lgl->stats->jlevel.slow.val >> 32;
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				red = blit & REDCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (red || tag == LRGCS)
					continue;
				other = blit >> RMSHFT;
				if (abs(other) < idx)
					continue;
				if (tag == BINCS) {
					res.b++;
				} else {
					assert(tag == TRNCS);
					other1 = *p;
					if (abs(other1) < idx)
						continue;
					res.t++;
				}
			}
		}
	lrg = 0;
	for (c = lgl->irr.start; c < lgl->irr.top; c = p + 1) {
		if (*(p = c) >= REMOVED)
			continue;
		while (*++p)
			;
		len = p - c;
		if (len <= 4)
			res.q++;
		else if (len <= 10)
			res.c1++;
		else if (len <= 100)
			res.c2++;
		else if (len <= 1000)
			res.c3++;
		else if (len <= 10000)
			res.c4++;
		else
			lrg++;
	}
	assert(res.b + res.t + res.q + res.c1 + res.c2 + res.c3 + res.c4 + lrg == res.cc);
	return res;
}

static int
lglintstrlen(int i)
{
	assert(i >= 0);
	if (i < 10)
		return 1;
	if (i < 100)
		return 2;
	if (i < 1000)
		return 3;
	if (i < 10000)
		return 4;
	if (i < 100000)
		return 5;
	if (i < 1000000)
		return 6;
	if (i < 10000000)
		return 7;
	if (i < 100000000)
		return 8;
	if (i < 1000000000)
		return 9;
	return 10;
}

#ifdef LGLSC14CLASSIFY
#include "sc14classify/sc14classify.h"
static int
lglsetoptif(LGL* lgl, const char* a, const char* b, const char* opt, int val)
{
	if (strcmp(a, b))
		return 0;
	lglprt(lgl, 1, "[classify-%d] setting '--%s=%d' since assuming '%s' bucket", lgl->stats->features, opt, val, a);
	lglsetopt(lgl, opt, val);
	return 1;
}
#endif

static void
lglshowfeatures(LGL* lgl)
{
	int size[NFEATURES], i;
	const int* p;
	char fmt[20];
	Features f;
	if (!lgl->opts->features.val)
		return;
	if (lgl->opts->verbose.val <= 0)
		return;
	if (lgl->stats->features >= lgl->opts->features.val)
		return;
	lglstart(lgl, &lgl->times->showfeatures);
	f = lglfeatures(lgl);
	p = (int*)&f;
	for (i = 0; i < NFEATURES; i++)
		size[i] = lglmax(strlen(featurenames[i]), lglintstrlen(p[i]));

	lglmsgstart(lgl, 0);
	fprintf(lgl->out, "[heatures-%d]", f.n);
	for (i = 0; i < NFEATURES; i++) {
		sprintf(fmt, " %%%ds", size[i]);
		fprintf(lgl->out, fmt, featurenames[i]);
	}
	lglmsgend(lgl);

	lglmsgstart(lgl, 0);
	fprintf(lgl->out, "[features-%d]", f.n);
	for (i = 0; i < NFEATURES; i++) {
		sprintf(fmt, " %%%dd", size[i]);
		fprintf(lgl->out, fmt, p[i]);
	}
	lglmsgend(lgl);

#ifdef LGLSC14CLASSIFY
	if (lgl->opts->classify.val && lgl->stats->features == 1) {
		const char* bucket = sc14classify(
			f.vo, f.vc, f.co, f.cc, f.b, f.t, f.q, f.c1, f.c2, f.c3, f.c4, f.x, f.a1, f.a2, f.g, f.j, f.c, f.o);
		lglprt(lgl, 1, "[classify-%d] classified as SAT Competition 2014 bucket '%s'", f.n, bucket);
		lglsetoptif(lgl, bucket, "2d-strip-packing", "block", 0),
			lglsetoptif(lgl, bucket, "2d-strip-packing", "cce", 0);
		lglsetoptif(lgl, bucket, "argumentation", "sweeprtc", 1);
		lglsetoptif(lgl, bucket, "crypto-aes", "restartint", 1000);
		lglsetoptif(lgl, bucket, "crypto-sha", "restartint", 100);
		lglsetoptif(lgl, bucket, "crypto-vpmc", "restartint", 4);
		lglsetoptif(lgl, bucket, "hardware-cec", "sweeprtc", 1);
		lglsetoptif(lgl, bucket, "hardware-velev", "block", 0), lglsetoptif(lgl, bucket, "hardware-velev", "cce", 0);
		lglsetoptif(lgl, bucket, "scheduling", "restartint", 1000);
		if (lgl->opts->classify.val < 2)
			goto DO_NOT_COMBINE_OPTIONS;
		lglsetoptif(lgl, bucket, "2d-strip-packing", "restartint", 1000);
		lglsetoptif(lgl, bucket, "2d-strip-packing", "scincinc", 50);
		lglsetoptif(lgl, bucket, "2d-strip-packing", "sweeprtc", 1);
		lglsetoptif(lgl, bucket, "bio", "restartint", 1000);
		lglsetoptif(lgl, bucket, "crypto-des", "restartint", 100);
		lglsetoptif(lgl, bucket, "crypto-md5", "restartint", 1000);
		lglsetoptif(lgl, bucket, "crypto-vmpc", "sweeprtc", 1);
		lglsetoptif(lgl, bucket, "hardware-cec", "restartint", 4);
		lglsetoptif(lgl, bucket, "hardware-cec", "scincincmin", 250);
		lglsetoptif(lgl, bucket, "software-bmc", "elmresched", 7);
		lglsetoptif(lgl, bucket, "software-bmc", "phase", -1);
		if (lgl->opts->classify.val < 3)
			goto DO_NOT_COMBINE_OPTIONS;
		lglsetoptif(lgl, bucket, "crypto-sha", "sweeprtc", 1);
		lglsetoptif(lgl, bucket, "diagnosis", "elmresched", 7);
		lglsetoptif(lgl, bucket, "planning", "reducefixed", 1),
			lglsetoptif(lgl, bucket, "planning", "reduceinit", 10000);
		lglsetoptif(lgl, bucket, "scheduling", "block", 0), lglsetoptif(lgl, bucket, "scheduling", "cce", 0);
		lglsetoptif(lgl, bucket, "scheduling", "elmresched", 7);
		lglsetoptif(lgl, bucket, "scheduling", "scincinc", 50);
	DO_NOT_COMBINE_OPTIONS:;
	}
#endif
	lgl->stats->features++;
	lglstop(lgl);
}

/*------------------------------------------------------------------------*/

static int
lglisimp(LGL* lgl)
{
	if (!lgl->opts->simplify.val)
		return 1;

	if (lglquatresolving(lgl))
		lglquatres(lgl);
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (lglternresolving(lgl) && !lglternres(lgl))
		return 0;
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (lglcarding(lgl) && !lglcard(lgl))
		return 0;
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (lglgaussing(lgl) && !lglgauss(lgl))
		return 0;
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (lgldecomposing(lgl) && !lgldecomp(lgl))
		return 0;
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (lglprobing(lgl) && !lglprobe(lgl))
		return 0;
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (lglunhiding(lgl) && !lglunhide(lgl))
		return 0;
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (lgltreducing(lgl) && !lgltrd(lgl))
		return 0;
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (lglblocking(lgl))
		lglblock(lgl);
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (lglcceing(lgl) && !lglcce(lgl))
		return 0;
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (lgleliminating(lgl) && !lglelim(lgl))
		return 0;
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (!lgltopgc(lgl))
		return 0;
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (lglbcaing(lgl))
		lglbca(lgl);
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (lglsweeping(lgl) && !lglsweep(lgl))
		return 0;
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (!lgl->allphaseset)
		lglphase(lgl);
	if (lglterminate(lgl))
		return 1;
	assert(!lgl->mt);

	if (lglqueuesorting(lgl))
		lglqueuesort(lgl);
	if (lglterminate(lgl))
		return 1;

	lgldefrag(lgl);
	if (lglterminate(lgl))
		return 1;

	lglshowscoredist(lgl);
	lglshowfeatures(lgl);

	return 1;
}

static void
lglupdsimpcinc(LGL* lgl, int red, int success)
{
	const char* type = "no";
	int inc = 0, div = 1, res;
	assert(red >= 0);
	if (red <= 100) {
		if (red > 0) {
			switch (lgl->opts->simpcintincdiv.val) {
				case 3:
					if (red >= 50)
						div = 4;
					else if (red >= 20)
						div = 3;
					else if (red >= 10)
						div = 2;
					break;
				case 2:
					div = red / 2 + 1;
					break;
				case 1:
					div = red / 1 + 1;
					break;
				case 0:
				default:
					assert(!lgl->opts->simpcintincdiv.val);
					break;
			}
		}
		inc = lgl->opts->simpcintinc.val;
		if (lgl->opts->simplify.val == 1) {
			type = "arithmetic";
		} else if (lgl->opts->simplify.val == 2) {
			if (inc < lgl->limits->simp.cinc)
				inc = lgl->limits->simp.cinc;
			type = "geometric";
		} else
			inc = 0;
	}
	res = inc / div;
	if (lgl->limits->simp.cinc - res <= INT_MAX)
		lgl->limits->simp.cinc += res;
	lglprt(lgl, 2, "[simplification-%d] new lgl->limits->simp.cinc %d", lgl->stats->simp.count, lgl->limits->simp.cinc);
	if (div > 1)
		lglprt(lgl,
			   1,
			   "[simplification-%d] %s conflict interval increment %d = %d / %d",
			   lgl->stats->simp.count,
			   type,
			   res,
			   inc,
			   div);
	else
		lglprt(lgl, 1, "[simplification-%d] full %s conflict interval increment %d", lgl->stats->simp.count, type, res);

	if (success && lgl->opts->simpcintmaxsoft.val >= 0 && lgl->limits->simp.cinc >= lgl->opts->simpcintmaxsoft.val) {
		lgl->limits->simp.cinc = lgl->opts->simpcintmaxsoft.val;
		lglprt(lgl,
			   1,
			   "[simplification-%d] "
			   "conflict interval soft limit %d reached",
			   lgl->stats->simp.count,
			   lgl->limits->simp.cinc);
	} else if (lgl->opts->simpcintmaxhard.val >= 0 && lgl->limits->simp.cinc >= lgl->opts->simpcintmaxhard.val) {
		lgl->limits->simp.cinc = lgl->opts->simpcintmaxhard.val;
		lglprt(lgl,
			   1,
			   "[simplification-%d] "
			   "conflict interval hard limit %d reached",
			   lgl->stats->simp.count,
			   lgl->limits->simp.cinc);
	}
}

static void
lglsethardsimplim(LGL* lgl)
{
	int64_t hard, delta;
	delta = lgl->limits->simp.confs - lgl->stats->confs;
	if (delta < 0)
		delta = 0;
	delta *= lgl->opts->simpincdelmaxfact.val;
	delta /= 100;
	if (delta < lgl->opts->simpincdelmaxmin.val)
		delta = lgl->opts->simpincdelmaxmin.val;
	if (LLMAX - delta < lgl->limits->simp.confs)
		hard = LLMAX;
	else
		hard = lgl->limits->simp.confs + delta;
	lgl->limits->simp.hard = hard;
	lglprt(lgl,
		   1, // TODO set this to 2
		   "[simplification-%d] hard conflict limit %lld (soft %lld + delta %lld)",
		   lgl->stats->simp.count,
		   (LGLL)lgl->limits->simp.hard,
		   (LGLL)lgl->limits->simp.confs,
		   (LGLL)delta);
}

static void
lglupdsimpint(LGL* lgl, int oldrem, int oldirr, int forced)
{
	int remvar, remirr, pcntred, simpcinc;
	int64_t pcntremvar64, pcntremirr64;
	int pcntremvar, pcntremirr;

	remvar = oldrem - lglrem(lgl);
	if (remvar < 0)
		remvar = 0;
	pcntremvar64 = (remvar > 0) ? ((1000ll * remvar) / oldrem + 5) / 10 : 0ll;
	assert(pcntremvar64 <= 100);
	pcntremvar = pcntremvar64;
	lglprt(lgl,
		   1,
		   "[simplification-%d] removed %d variables %.1f%% "
		   "(%d remain %0.f%%)",
		   lgl->stats->simp.count,
		   remvar,
		   lglpcnt(remvar, oldrem),
		   lglrem(lgl),
		   lglpcnt(lglrem(lgl), lgl->limits->inc.vars.start));

	remirr = oldirr - lgl->stats->irr.clauses.cur;
	if (remirr < 0)
		remirr = 0;
	pcntremirr64 = (remirr > 0) ? ((1000ll * remirr) / oldirr + 5) / 10 : 0ll;
	assert(pcntremirr64 <= 100);
	pcntremirr = pcntremirr64;
	lglprt(lgl,
		   1,
		   "[simplification-%d] removed %d irredundant clauses %.1f%% "
		   "(%d remain %.0f%%)",
		   lgl->stats->simp.count,
		   remirr,
		   lglpcnt(remirr, oldirr),
		   lgl->stats->irr.clauses.cur,
		   lglpcnt(lgl->stats->irr.clauses.cur, lgl->limits->inc.clauses.start));

	pcntred = lglmax(pcntremvar, pcntremirr);
	lglprt(lgl,
		   1,
		   "[simplification-%d] maximum reduction of %d%% = max (%d%%, %d%%)",
		   lgl->stats->simp.count,
		   pcntred,
		   pcntremvar,
		   pcntremirr);

	if (forced) {
		simpcinc = 0;
		lglprt(lgl,
			   1,
			   "[simplification-%d] forced so keeping old conflict interval %d",
			   lgl->stats->simp.count,
			   lgl->limits->simp.cinc);
	} else if (lgl->stats->confs < lgl->limits->simp.confs && (simpcinc = lgl->limits->simp.cinc)) {
		lglprt(lgl,
			   1,
			   "[simplification-%d] keeping old conflict interval %d "
			   "(non-conflict triggered simplification)",
			   lgl->stats->simp.count,
			   lgl->limits->simp.cinc);
	} else if (remvar > 0 && pcntred >= lgl->opts->simprtc.val) {
		int64_t scaledcinc;
		int factor;
		lglprt(lgl,
			   1,
			   "[simplification-%d] large reduction %d%% >= %d%% limit",
			   lgl->stats->simp.count,
			   pcntred,
			   lgl->opts->simprtc.val);
		factor = lgl->opts->simpidiv.val - lgl->stats->simp.count;
		if (factor <= 0)
			factor = 1;
		simpcinc = lgl->opts->simpcintdelay.val / factor;
		scaledcinc = lgl->stats->confs / lgl->opts->simpiscale.val;
		if (scaledcinc > lgl->limits->simp.cinc)
			scaledcinc = lgl->limits->simp.cinc;
		if (scaledcinc < simpcinc) {
			lglprt(lgl,
				   1,
				   "[simplification-%d] next conflict interval %d = %d/%d",
				   lgl->stats->simp.count,
				   simpcinc,
				   lgl->opts->simpcintdelay.val,
				   factor);
		} else {
			assert(scaledcinc <= INT_MAX);
			simpcinc = (int)scaledcinc;
			lglprt(lgl,
				   1,
				   "[simplification-%d] next conflict interval %d = min (%lld/%d, %d)",
				   lgl->stats->simp.count,
				   simpcinc,
				   (LGLL)lgl->stats->confs,
				   lgl->opts->simpiscale.val,
				   lgl->limits->simp.cinc);
		}
	} else {
		lglupdsimpcinc(lgl, pcntred, remvar || remirr);
		simpcinc = lgl->limits->simp.cinc;
		lglprt(lgl, 1, "[simplification-%d] new conflict interval %d", lgl->stats->simp.count, simpcinc);
	}

	if (forced) {
		lglprt(lgl,
			   1,
			   "[simplification-%d] conflict limit remains at %lld (hard %lld)",
			   lgl->stats->simp.count,
			   (LGLL)lgl->limits->simp.confs,
			   (LGLL)lgl->limits->simp.hard);
	} else {
		int penalty, pensimpcinc;
		if (!lgl->opts->simpintsizepen.val || lgl->stats->irr.clauses.cur < 1000000)
			penalty = 1;
		else if (lgl->stats->irr.clauses.cur < 10000000)
			penalty = 2;
		else /*  lgl->stats->irr.clauses.cur >=  10000000) */
			penalty = 4;
		if (INT_MAX / penalty < simpcinc)
			pensimpcinc = INT_MAX;
		else
			pensimpcinc = penalty * simpcinc;
		lglprt(lgl,
			   1,
			   "[simplification-%d] penalized conflict interval %d = %d * %d",
			   lgl->stats->simp.count,
			   pensimpcinc,
			   penalty,
			   simpcinc);
		assert(pensimpcinc >= 0);
		lgl->limits->simp.confs = lgl->stats->confs + pensimpcinc;
		assert(lgl->limits->simp.confs >= 0);
		lglsethardsimplim(lgl);
		lglprt(lgl,
			   1,
			   "[simplification-%d] new conflict limit %lld (hard %lld)",
			   lgl->stats->simp.count,
			   (LGLL)lgl->limits->simp.confs,
			   (LGLL)lgl->limits->simp.hard);
	}

	if (lgl->stats->confs >= lgl->limits->simp.confs) {
		lgl->limits->simp.confs = lgl->stats->confs + 1;
		if (lgl->limits->simp.hard < lgl->limits->simp.confs)
			lgl->limits->simp.hard = lgl->limits->simp.confs;
		lglprt(lgl,
			   1,
			   "[simplification-%d] "
			   "fixed conflict limit %d (wait at least for one conflict)",
			   lgl->stats->simp.count,
			   lgl->stats->confs);
	}

	if (forced == 1 && lgl->limits->simp.itinc < lgl->opts->simpitintinclim.val) {
		if (lgl->limits->simp.itinc < INT_MAX / 10)
			lgl->limits->simp.itinc *= 10;
		else
			lgl->limits->simp.itinc = INT_MAX;
		if (lgl->limits->simp.itinc > lgl->opts->simpitintinclim.val)
			lgl->limits->simp.itinc = lgl->opts->simpitintinclim.val;
		lglprt(
			lgl, 1, "[simplification-%d] new iteration interval %d", lgl->stats->simp.count, lgl->limits->simp.itinc);
	}
	lgl->limits->simp.its = lgl->stats->its.count + lgl->limits->simp.itinc;

	if (forced == 2 && lgl->limits->simp.binc < lgl->opts->simpbintinclim.val) {
		if (lgl->limits->simp.binc < INT_MAX / 10)
			lgl->limits->simp.binc *= 10;
		else
			lgl->limits->simp.binc = INT_MAX;
		if (lgl->limits->simp.binc > lgl->opts->simpbintinclim.val)
			lgl->limits->simp.binc = lgl->opts->simpbintinclim.val;
		lglprt(lgl, 1, "[simplification-%d] new binary interval %d", lgl->stats->simp.count, lgl->limits->simp.binc);
	}
	lgl->limits->simp.bin = lgl->stats->bins + lgl->limits->simp.binc;

	if (forced == 3 && lgl->limits->simp.tinc < lgl->opts->simptintinclim.val) {
		if (lgl->limits->simp.tinc < INT_MAX / 10)
			lgl->limits->simp.tinc *= 10;
		else
			lgl->limits->simp.tinc = INT_MAX;
		if (lgl->limits->simp.tinc > lgl->opts->simptintinclim.val)
			lgl->limits->simp.tinc = lgl->opts->simptintinclim.val;
		lglprt(lgl, 1, "[simplification-%d] new ternary interval %d", lgl->stats->simp.count, lgl->limits->simp.tinc);
	}
	lgl->limits->simp.trn = lgl->stats->trns + lgl->limits->simp.tinc;

	lgl->limits->simp.vars = lglrem(lgl);
	lgl->repforcehead = 1;
	if (lgl->wait > 0)
		lgl->wait--;

#ifndef NLGLFILES
	{
		static FILE* simpsfile = 0;
		if (!simpsfile)
			simpsfile = fopen("/tmp/simps", "w");
		fprintf(simpsfile, "%lld\n", (LGLL)lgl->stats->confs);
		fflush(simpsfile);
	}
#endif
}

static int
lglpreprocessing(LGL* lgl, int forced)
{
	int res, oldrem, oldirr;
	if (lgl->mt)
		return 0;
	assert(!lgl->searching);
	lgl->stats->simp.count++;
	oldrem = lglrem(lgl);
	oldirr = lgl->stats->irr.clauses.cur;
	res = lglisimp(lgl);
	lglupdsimpint(lgl, oldrem, oldirr, forced);
	assert(res == !lgl->mt);
	return res;
}

static int
lglsimplimhit(LGL* lgl, int* forcedptr)
{
	int64_t n, o, d;
	int a, r, res = 0;
	;
	if (!lgl->opts->simplify.val)
		return 0;
	if (!lgl->opts->inprocessing.val && lgl->stats->simp.count)
		return 0;
	if (lgl->stats->confs < lgl->limits->simp.hard) {
		if (lgl->opts->simpitintdecdelay.val && lgl->stats->its.avg.diff.smoothed.val < 0)
			return 0;
		if (lgl->opts->simpjleveldecdelay.val && lgl->stats->jlevel.diff.smoothed.val < 0)
			return 0;
	}
	if (lgl->stats->confs >= lgl->limits->simp.confs) {
		lglprt(lgl, 1, "");
		lglprt(lgl,
			   1,
			   "[simplification-%d] "
			   "limit %lld conflicts (hard %lld) hit after %lld conflicts",
			   lgl->stats->simp.count + 1,
			   (LGLL)lgl->limits->simp.confs,
			   (LGLL)lgl->limits->simp.hard,
			   (LGLL)lgl->stats->confs);
		lgl->stats->simp.limhit.confs++;
		*forcedptr = 0;
		res = 1;
	}
	if (!res && lgl->stats->trns >= lgl->limits->simp.trn) {
		lglprt(lgl, 1, "");
		lglprt(lgl,
			   1,
			   "[simplification-%d] limit of %lld ternary hit after %lld ternaries",
			   lgl->stats->simp.count + 1,
			   (LGLL)lgl->limits->simp.trn,
			   (LGLL)lgl->stats->trns);
		lgl->stats->simp.limhit.trn++;
		*forcedptr = 3;
		res = 1;
	}
	if (!res && lgl->stats->bins >= lgl->limits->simp.bin) {
		lglprt(lgl, 1, "");
		lglprt(lgl,
			   1,
			   "[simplification-%d] limit of %lld binary hit after %lld binaries",
			   lgl->stats->simp.count + 1,
			   (LGLL)lgl->limits->simp.bin,
			   (LGLL)lgl->stats->bins);
		lgl->stats->simp.limhit.bin++;
		*forcedptr = 2;
		res = 1;
	}
	if (!res && lgl->stats->its.count >= lgl->limits->simp.its) {
		lglprt(lgl, 1, "");
		lglprt(lgl,
			   1,
			   "[simplification-%d] limit of %lld iterations hit after %d iterations",
			   lgl->stats->simp.count + 1,
			   (LGLL)lgl->limits->simp.its,
			   lgl->stats->its.count);
		lgl->stats->simp.limhit.its++;
		*forcedptr = 1;
		res = 1;
	}
	if (!res && (o = lgl->limits->simp.vars) && (n = lglrem(lgl)) >= lgl->opts->simpvarlim.val) {
		d = n - o;
		r = 100 * d;
		r /= o;
		if (r <= INT_MIN || r > INT_MAX)
			a = INT_MAX;
		else if (r < 0)
			a = -r;
		else
			a = r;
		if (a >= lgl->opts->simpvarchg.val) {
			lglprt(lgl, 1, "");
			lglprt(lgl,
				   1,
				   "[simplification-%d] limit hit: "
				   "remaining variables changed from %lld to %lld by %d%%",
				   lgl->stats->simp.count + 1,
				   (LGLL)o,
				   (LGLL)n,
				   r);
			lgl->stats->simp.limhit.vars++;
			*forcedptr = -1;
			res = 1;
		}
	}
	return res;
}

static int
lglinprocessing(LGL* lgl)
{
	int res, oldrem, oldirr, forced;
	assert(lgl->searching);
	if (!lglsimplimhit(lgl, &forced))
		return !lgl->mt;
	lgl->stats->simp.count++;
	lglstart(lgl, &lgl->times->inprocessing);
	oldrem = lglrem(lgl);
	oldirr = lgl->stats->irr.clauses.cur;
	res = lglisimp(lgl);
	lglupdsimpint(lgl, oldrem, oldirr, forced);
	lglstop(lgl);
	assert(res == !lgl->mt);
	return res;
}

static int
lglbcptop(LGL* lgl)
{
	int res;
	if (lglbcp(lgl))
		res = 1;
	else {
#ifndef NDEBUG
		int tmp =
#endif
			lglana(lgl);
		assert(!tmp);
		if (lgl->conf.lit) {
			LOG(1, "top level propagation produces inconsistency");
			lglmt(lgl);
		} else
			assert(lgl->failed);
		res = 0;
	}
	return res;
}

static int
lglimhit(LGL* lgl, Lim* lim)
{
	if (!lim)
		return 0;
	if (lim->decs >= 0 && lgl->stats->decisions >= lim->decs) {
		lglprt(
			lgl, 1, "[limits] decision limit %lld hit at %lld decisions", (LGLL)lim->decs, (LGLL)lgl->stats->decisions);
		return 1;
	}
	if (lim->confs >= 0 && lgl->stats->confs >= lim->confs) {
		lglprt(lgl, 1, "[limits] conflict limit %lld hit at %lld conflicts", (LGLL)lim->confs, (LGLL)lgl->stats->confs);
		return 1;
	}
	if (lim->props >= 0 && lgl->stats->props.search >= lim->props) {
		lglprt(lgl,
			   1,
			   "[limits] propagation limit %lld hit at %lld propagations",
			   (LGLL)lim->props,
			   (LGLL)lgl->stats->props.search);
		return 1;
	}
	return 0;
}

static int
lgloop(LGL* lgl, Lim* lim)
{
	unsigned confs = 0;
	for (;;) {
		if (lglbcpsearch(lgl) && lglinprocessing(lgl)) {
			if (lglterminate(lgl))
				return 0;
			if (!lglsyncunits(lgl))
				return 20;
			if (!lglsyncls(lgl))
				continue;
			if (lglfailedass(lgl))
				return 20;
			if (lglreducing(lgl))
				lglreduce(lgl, 0);
			if (lgldefragmenting(lgl))
				lgldefrag(lgl);
			if (lglimhit(lgl, lim))
				return 0;
			if (lglocsing(lgl))
				lglocs(lgl);
			if (lglrestarting(lgl)) {
				lglrestart(lgl);
				continue;
			}
			if (lglpruning(lgl)) {
				lglprune(lgl);
				continue;
			}
			if (!lgldecide(lgl))
				return 10;
		} else if (!lglana(lgl))
			return 20;
		else if (!(++confs & 15) && lglterminate(lgl))
			return 0;
	}
}

static int
lglsearch(LGL* lgl, Lim* lim)
{
	int res;
	assert(!lgl->searching);
	lgl->searching = 1;
	lglstart(lgl, &lgl->times->search);
	res = lgloop(lgl, lim);
	assert(lgl->searching);
	lgl->searching = 0;
	lglstop(lgl);
	return res;
}

static int
lgltopsimp(LGL* lgl, int forcesimp)
{
	assert(lgl->state == READY);
	if (lgl->mt)
		return 20;
	if (lglfailedass(lgl))
		return 20;
	if (!lglbcptop(lgl))
		return 20;
	if (lgl->mt || lglfailedass(lgl))
		return 20;
	if (lglterminate(lgl))
		return 0;
	lglshowfeatures(lgl);
	if ((forcesimp || lglsimplimhit(lgl, &forcesimp)) && !lglpreprocessing(lgl, forcesimp))
		return 20;
	if (lglfailedass(lgl))
		return 20;
	lglrep(lgl, 2, 's');
	return 0;
}

static int
lglsolve(LGL* lgl, Lim* lim, int forcesimp)
{
	int res;
	lglstart(lgl, &lgl->times->preprocessing);
	res = lgltopsimp(lgl, forcesimp);
	lglstop(lgl);
	if (res) {
		assert(res == 20);
		return res;
	}
	return lglsearch(lgl, lim);
}

static void
lglincsetup(LGL* lgl)
{

	if (lgl->opts->incsavevisits.val)
		lgl->limits->inc.visits = lgl->stats->visits.search;

	assert(lgl->limits->inc.clauses.add <= lgl->stats->irr.clauses.add);
	if (lgl->limits->inc.clauses.add < lgl->stats->irr.clauses.add) {
		lgl->limits->inc.clauses.add = lgl->stats->irr.clauses.add;
		lgl->elmrtc = lgl->blkrtc = lgl->ccertc = 0;
		lgl->wait = lgl->opts->waitmax.val;
	}

	lgl->limits->inc.clauses.start = lgl->stats->irr.clauses.cur;
	lgl->limits->inc.vars.start = lglrem(lgl);

	lgl->limits->prune.decisions = lgl->stats->decisions + lgl->limits->prune.inc;

	lglinitredl(lgl);

	lgl->stats->locs.min = INT_MAX;
	lglupdlocslim(lgl, 0);

	CLR(lgl->limits->restart);
	lgl->limits->restart.confs = lgl->stats->confs;

	if (lgl->opts->incredcint.val > 1) {
		lgl->limits->simp.cinc /= lgl->opts->incredcint.val;
		lglprt(lgl, 1, "[incremental-setup] conflict interval increment reduced to %d", lgl->limits->simp.cinc);
	}
	if (lgl->opts->incredconfslim.val) {
		lgl->limits->simp.confs = ((100 - lgl->opts->incredconfslim.val) * lgl->limits->simp.confs) / 100;
		lglsethardsimplim(lgl);
		lglprt(lgl,
			   1,
			   "[incremental-setup] conflict limit reduced to %lld (hard %lld)",
			   (LGLL)lgl->limits->simp.confs,
			   (LGLL)lgl->limits->simp.hard);
	}

	if (!lgl->limits->simp.itinc)
		lgl->limits->simp.itinc = lgl->opts->simpitintinc.val;
	lgl->limits->simp.its = lgl->stats->its.count + lgl->opts->simpitintinc.val;

	if (!lgl->limits->simp.binc)
		lgl->limits->simp.binc = lgl->opts->simpbintinc.val;
	lgl->limits->simp.bin = lgl->stats->bins + lgl->opts->simpbintinc.val;

	if (!lgl->limits->simp.tinc)
		lgl->limits->simp.tinc = lgl->opts->simptintinc.val;
	lgl->limits->simp.trn = lgl->stats->trns + lgl->opts->simptintinc.val;

	lglinitmacd(lgl,
				&lgl->stats->jlevel,
				lgl->opts->jlevelmacdfast.val,
				lgl->opts->jlevelmacdslow.val,
				lgl->opts->jlevelmacdsmooth.val);

	lglinitema(lgl, &lgl->stats->tlevel, lgl->opts->tlevelema.val);

	lglinitmacd(lgl,
				&lgl->stats->glue,
				lgl->opts->gluemacdfast.val,
				lgl->opts->gluemacdslow.val,
				lgl->opts->gluemacdsmooth.val);

	lglinitavg(&lgl->stats->avglue);

	lglinitmacd(lgl,
				&lgl->stats->its.avg,
				lgl->opts->itsmacdfast.val,
				lgl->opts->itsmacdslow.val,
				lgl->opts->itsmacdsmooth.val);

	lglinitema(lgl, &lgl->stats->stability.avg, lgl->opts->stabema.val);

	CLR(lgl->limits->elm.del);
	CLR(lgl->limits->blk.del);

	CLR(lgl->limits->trd.del);
	CLR(lgl->limits->unhd.del);
	CLR(lgl->limits->ternres.del);
	CLR(lgl->limits->cce.del);
	CLR(lgl->limits->card.del);

	CLR(lgl->limits->gauss.del);

	CLR(lgl->limits->prb.simple.del);
	CLR(lgl->limits->prb.basic.del);
	CLR(lgl->limits->prb.treelook.del);

	CLR(lgl->limits->bca.del);
}

static void
lglsetup(LGL* lgl)
{
	if (lgl->setuponce)
		goto DONE;
	lgl->limits->dfg = lgl->stats->pshwchs + lgl->opts->defragint.val;

	lgl->limits->blk.irrprgss = -1;
	lgl->limits->elm.irrprgss = -1;
	lgl->limits->term.steps = -1;

	lgl->limits->prune.inc = lgl->opts->pruneinit.val;

	lgl->rng.w = (unsigned)lgl->opts->seed.val;
	lgl->rng.z = ~lgl->rng.w;
	lgl->rng.w <<= 1;
	lgl->rng.z <<= 1;
	lgl->rng.w += 1;
	lgl->rng.z += 1;
	lgl->rng.w *= 2019164533u, lgl->rng.z *= 1000632769u;

	assert(!lgl->stats->decisions);
	lgl->limits->randec += lgl->opts->randecint.val / 2;
	lgl->limits->randec += lglrand(lgl) % lgl->opts->randecint.val;

	assert(!lgl->stats->decisions);
	lgl->limits->randphase += lgl->opts->randphaseint.val / 2;
	lgl->limits->randphase += lglrand(lgl) % lgl->opts->randphaseint.val;

	lgl->limits->simp.vars = lglrem(lgl);

	lgl->setuponce = 1;
	lglchkenv(lgl);
DONE:
	lglinitscores(lgl);
	lglincsetup(lgl);
	TRANS(READY);
}

static void
lglinitsolve(LGL* lgl)
{
	if (lgl->state != READY)
		lglsetup(lgl);
	lglredvars(lgl);
	lglfitstk(lgl, &lgl->irr);
#ifndef NCHKSOL
	lglfitstk(lgl, &lgl->orig);
#endif
	lglrep(lgl, 1, '*');
}

static void
lgleassign(LGL* lgl, int lit)
{
	Ext* ext;
	ext = lglelit2ext(lgl, lit);
	LOG(3, "external assign %d ", lit);
	ext->val = lglsgn(lit);
}

static void
lglcomputechanged(LGL* lgl)
{
	Ext* ext;
	int eidx;
	lgl->changed = 0;
	for (eidx = 1; eidx <= lgl->maxext; eidx++) {
		ext = lglelit2ext(lgl, eidx);
		if (ext->oldval && ext->oldval != ext->val)
			lgl->changed++;
		ext->oldval = ext->val;
	}
	LOG(1, "changed %d assignments in extension", lgl->changed);
}

static void
lglextend(LGL* lgl)
{
	int *p, lit, eidx, ilit, next, satisfied, val, *start, erepr, equiv;
	Ext *ext, *extrepr;
	assert(lgl->state & SATISFIED);
	assert(!(lgl->state & EXTENDED));
	lgleunassignall(lgl);
	for (equiv = 0; equiv <= 1; equiv++) {
		if (equiv)
			LOG(1, "initializing assignment of non-representative externals");
		else
			LOG(1, "initializing assignment of external representatives");
		for (eidx = 1; eidx <= lgl->maxext; eidx++) {
			ext = lglelit2ext(lgl, eidx);
			if (!ext->imported)
				continue;
			if (equiv != ext->equiv)
				continue;
			assert(!ext->val);
			if (ext->equiv) {
				erepr = lglerepr(lgl, eidx);
				LOG(3, "initializing external %d assignment from external representative %d", eidx, erepr);
				assert(erepr != eidx);
				extrepr = lglelit2ext(lgl, erepr);
				if (!(val = extrepr->val)) {
					ilit = extrepr->repr;
					if (ilit) {
						LOG(3, "using external %d to internal %d mapping", abs(erepr), ilit);
						val = lglcval(lgl, ilit);
					} else
						LOG(3, "external %d without internal representative", abs(erepr));
				}
				if (erepr < 0)
					val = -val;
			} else if ((ilit = ext->repr)) {
				LOG(3, "using external %d to internal %d mapping", eidx, ilit);
				val = lglcval(lgl, ilit);
			} else {
				LOG(3, "external %d without internal representative", eidx);
				val = 0;
			}
			lit = (val > 0) ? eidx : -eidx;
			lgleassign(lgl, lit);
		}
	}
	start = lgl->extend.start;
	p = lgl->extend.top;
	if (p == start)
		goto SKIP;
	assert(p > start);
	p--;
	assert(!*p);
	while (p > start) {
#ifndef NLGLOG
		if (lgl->opts->log.val >= 4) {
			int* q;
			for (q = p; q > start && q[-1]; q--)
				;
			LOGCLS(4, q, "next sigma clause to consider");
		}
#endif
		satisfied = 0;
		next = 0;
		do {
			lit = next;
			if (p == start)
				next = 0;
			else
				next = *--p;
			if (!lit || satisfied)
				continue;
			val = lglederef(lgl, lit);
			assert(!next || val);
			if (val > 0) {
				LOG(4, "sigma clause satisfied by %d", lit);
				satisfied = 1;
			}
		} while (next);
		assert(lit);
		if (satisfied)
			continue;
		lgleassign(lgl, lit);
	}
SKIP:
	lglcomputechanged(lgl);
	TRANS(EXTENDED);
}

void
lglsetphases(LGL* lgl)
{
	int elit, phase;
	REQINITNOTFORKED();
	TRAPI("setphases");
	REQUIRE(SATISFIED | EXTENDED);
	if (!(lgl->state & EXTENDED))
		lglextend(lgl);
	for (elit = 1; elit <= lgl->maxext; elit++) {
		phase = lglederef(lgl, elit);
		assert(abs(phase) <= 1);
		lglesetphase(lgl, elit, phase);
	}
	if (lgl->clone)
		lglsetphases(lgl->clone);
}

#ifndef NCHKSOL
#include <signal.h>
#include <unistd.h>
static void
lglchksol(LGL* lgl)
{
	int *p, *c, *eoo = lgl->orig.top, lit, satisfied, sign, idx;
	unsigned bit;
	Ext* ext;
	assert(lglmtstk(&lgl->orig) || !eoo[-1]);
	assert(lgl->state == EXTENDED);
	for (c = lgl->orig.start; c < eoo; c = p + 1) {
		satisfied = 0;
		for (p = c; (lit = *p); p++)
			if (!satisfied && lglederef(lgl, lit) > 0)
				satisfied = 1;
		if (satisfied)
			continue;
		fflush(stderr);
		lglmsgstart(lgl, 0);
		fprintf(lgl->out, "unsatisfied original external clause");
		for (p = c; (lit = *p); p++)
			fprintf(lgl->out, " %d", lit);
		lglmsgend(lgl);
		ASSERT(satisfied);
		usleep(1000);
		abort(); // NOTE: not 'lglabort' on purpose !!
	}
	for (idx = 1; idx <= lgl->maxext; idx++) {
		ext = lglelit2ext(lgl, idx);
		if (!ext->assumed)
			continue;
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			bit = 1u << (lit < 0);
			if (!(ext->assumed & bit))
				continue;
			if (lglederef(lgl, lit) > 0)
				continue;
			lglprt(lgl, 0, "unsatisfied assumption %d", lit);
			assert(lglederef(lgl, lit) > 0);
			usleep(1000);
			abort(); // DITO: not 'lglabort' on purpose !!
		}
	}
}
#endif

static void
lglclass(LGL* lgl, LGL* from)
{
	Ext *extfrom, *extlgl;
	int eidx, cloned;
	REQINITNOTFORKED();
	ABORTIF(lgl->mt, "can not clone assignment into inconsistent manager");
	ABORTIF(!from, "uninitialized 'from' solver");
	ABORTIF(!(from->state & (SATISFIED | EXTENDED)), "require 'from' state to be (SATISFIED | EXTENDED)");
	ABORTIF(from->maxext != lgl->maxext, "can not clone assignments for different sets of variables");
	if (!(from->state & EXTENDED))
		lglextend(from);
	lglreset(lgl);
	lgleunassignall(lgl);
	cloned = lgl->changed = 0;
	for (eidx = 1; eidx <= lgl->maxext; eidx++) {
		extlgl = lglelit2ext(lgl, eidx);
		if (!extlgl->imported)
			continue;
		extfrom = lglelit2ext(from, eidx);
		ABORTIF(!extfrom->imported, "can not clone assignment of literal imported only by 'to'");
		assert(extfrom->val == 1 || extfrom->val == -1);
		lgleassign(lgl, extfrom->val * eidx);
		cloned++;
	}
	lglcomputechanged(lgl);
	lglprt(lgl, 1, "[class] cloned %d assignments (%d changed)", cloned, lgl->changed);
	TRANS(EXTENDED);
#ifndef NCHKSOL
	lglchksol(lgl);
#endif
}

static void
lglnegass(LGL* lgl)
{
	const int* p;
	Stk eassume;
	REQINITNOTFORKED();
	TRAPI("negass");
	if (lgl->mt)
		return;
	CLR(eassume);
	for (p = lgl->eassume.start; p < lgl->eassume.top; p++)
		lglpushstk(lgl, &eassume, *p);
	for (p = eassume.start; p < eassume.top; p++)
		lgleadd(lgl, -*p);
	lgleadd(lgl, 0);
	assert(lglmtstk(&lgl->eassume));
	for (p = eassume.start; p < eassume.top; p++)
		lglassume(lgl, *p);
	lglrelstk(lgl, &eassume);
	lgluse(lgl);
	if (lgl->clone)
		lglnegass(lgl->clone);
}

static int
lglcompactify(LGL* lgl, int res)
{
	if (!lgl->opts->compact.val)
		return 0;
	if (!res)
		return 1;
	if (res == 20)
		return 1;
	assert(res == 10);
	return lgl->opts->compact.val >= 2;
}

static int
lglisat(LGL* lgl, Lim* lim, int simpits)
{
	int res, count, reported = lgl->stats->reported;
	lglreset(lgl);
	lglinitsolve(lgl);
	res = lglsolve(lgl, lim, 0);
	for (count = 0; !res && count < simpits; count++) {
		lglprt(lgl,
			   1,
			   "[simplification-%d] next forced simplification iteration %d",
			   lgl->stats->simp.count + 1,
			   count + 1);
		res = lglsolve(lgl, lim, 1);
	}
	if (lglcompactify(lgl, res))
		lglcompact(lgl);
	if (!res) {
		TRANS(UNKNOWN);
		lglrep(lgl, 1, '?');
	}
	if (res == 10) {
		TRANS(SATISFIED);
		lglrep(lgl, 1, '1');
	}
	if (res == 20) {
		TRANS(UNSATISFIED);
		if (!lgl->level && !lgl->mt)
			assert(lgl->failed);
		lglrep(lgl, 1, '0');
	}
	if (reported != lgl->stats->reported)
		lglflshrep(lgl);
	if (res == 10)
		lglextend(lgl);
#ifndef NCHKSOL
	if (res == 10)
		lglchksol(lgl);
#endif
	return res;
}

int
lglunclone(LGL* lgl, LGL* from)
{
	int res;
	REQINITNOTFORKED();
	if (lgl->mt)
		return 20;
	ABORTIF(!from, "uninitialized 'from' solver");
	if (from->mt || (from->state & UNSATISFIED)) {
		lglprt(lgl, 1, "[unclone] unsatisfied state");
		lglnegass(lgl);
		res = lglisat(lgl, 0, 0);
	} else if (from->state & (SATISFIED | EXTENDED)) {
		lglprt(lgl, 1, "[unclone] satisfied state");
		lglclass(lgl, from);
		res = 10;
	} else {
		lglprt(lgl, 1, "[unclone] unknown state");
		lglreset(lgl);
		TRANS(UNKNOWN);
		res = 0;
	}
	return res;
}

#define CHKCLONESTATS(STATS)                                                                                           \
	do {                                                                                                               \
		assert(clone->stats->STATS == orig->stats->STATS);                                                             \
	} while (0)

static void
lglchkclonesamestats(LGL* orig)
{
#ifndef NDEBUG
	LGL* clone = orig->clone;
	assert(clone);
	assert(clone->state == orig->state);
	CHKCLONESTATS(confs);
	CHKCLONESTATS(decisions);
	CHKCLONESTATS(bytes.current);
	CHKCLONESTATS(bytes.max);
	CHKCLONESTATS(props.search);
	CHKCLONESTATS(props.simp);
	CHKCLONESTATS(props.lkhd);
	CHKCLONESTATS(visits.search);
	CHKCLONESTATS(visits.simp);
	CHKCLONESTATS(visits.lkhd);
#endif
	(void)orig;
}

#define CHKCLONE()                                                                                                     \
	do {                                                                                                               \
		if (!lgl->clone)                                                                                               \
			break;                                                                                                     \
		lglchkclonesamestats(lgl);                                                                                     \
	} while (0)

#define CHKCLONENORES(FUN)                                                                                             \
	do {                                                                                                               \
		if (!lgl->clone)                                                                                               \
			break;                                                                                                     \
		FUN(lgl->clone);                                                                                               \
		CHKCLONE();                                                                                                    \
	} while (0)

#define CHKCLONERES(FUN, RES)                                                                                          \
	do {                                                                                                               \
		int CLONERES;                                                                                                  \
		if (!lgl->clone)                                                                                               \
			break;                                                                                                     \
		CLONERES = FUN(lgl->clone);                                                                                    \
		ABORTIF(CLONERES != (RES),                                                                                     \
				"%s (lgl->clone) = %d differs from %s (lgl) = %d",                                                     \
				__FUNCTION__,                                                                                          \
				CLONERES,                                                                                              \
				__FUNCTION__,                                                                                          \
				(RES));                                                                                                \
		CHKCLONE();                                                                                                    \
	} while (0)

#define RETURN(FUN, RES)                                                                                               \
	do {                                                                                                               \
		TRAPI("return %d", (RES));                                                                                     \
		CHKCLONERES(FUN, (RES));                                                                                       \
	} while (0)

#define CHKCLONEARGRES(FUN, ARG, RES)                                                                                  \
	do {                                                                                                               \
		int CLONERES;                                                                                                  \
		if (!lgl->clone)                                                                                               \
			break;                                                                                                     \
		CLONERES = FUN(lgl->clone, (ARG));                                                                             \
		ABORTIF(CLONERES != (RES),                                                                                     \
				"%s (lgl->clone, %d) = %d differs from %s (lgl, %d) = %d",                                             \
				__FUNCTION__,                                                                                          \
				(ARG),                                                                                                 \
				CLONERES,                                                                                              \
				__FUNCTION__,                                                                                          \
				(ARG),                                                                                                 \
				(RES));                                                                                                \
		CHKCLONE();                                                                                                    \
	} while (0)

#define RETURNARG(FUN, ARG, RES)                                                                                       \
	do {                                                                                                               \
		TRAPI("return %d", (RES));                                                                                     \
		CHKCLONEARGRES(FUN, (ARG), (RES));                                                                             \
	} while (0)

static void
lglsetlim(LGL* lgl, Lim* lim)
{
	int64_t clim, dlim, plim, confs, decs, props, delay, delayed;
	if ((dlim = lgl->opts->dlim.val) < 0) {
		lim->decs = -1;
		lglprt(lgl, 1, "[limits] no decision limit");
	} else {
		decs = lgl->stats->decisions;
		lim->decs = (decs >= LLMAX - dlim) ? LLMAX : decs + dlim;
		lglprt(lgl, 1, "[limits] decision limit %lld after %lld decisions", (LGLL)lim->decs, (LGLL)decs);
	}
	if ((clim = lgl->opts->clim.val) < 0) {
		lim->confs = -1;
		lglprt(lgl, 1, "[limits] no conflict limit");
	} else {
		confs = lgl->stats->confs;
		lim->confs = (confs >= LLMAX - clim) ? LLMAX : confs + clim;
		lglprt(lgl, 1, "[limits] conflict limit %lld after %lld conflicts", (LGLL)lim->confs, (LGLL)confs);
	}
	if ((plim = lgl->opts->plim.val) < 0) {
		lim->props = -1;
		lglprt(lgl, 1, "[limits] no propagation limit");
	} else {
		plim *= 1000;
		props = lgl->stats->props.search;
		lim->props = (props >= LLMAX - plim) ? LLMAX : props + plim;
		lglprt(lgl, 1, "[limits] propagation limit %lld after %lld propagations", (LGLL)lim->props, (LGLL)props);
	}
	if ((delay = lgl->opts->simpinitdelay.val) > 0) {
		delayed = lgl->stats->confs + delay;
		if (delayed > lgl->limits->simp.confs) {
			lgl->limits->simp.confs = delayed;
			lglsethardsimplim(lgl);
			lglprt(lgl,
				   1,
				   "[limits] simplification delayed by %lld to %lld conflicts (hard %lld)",
				   (LGLL)delay,
				   (LGLL)lgl->limits->simp.confs,
				   (LGLL)lgl->limits->simp.hard);
		} else
			lglprt(lgl, 1, "[limits] simplification conflict limit already exceeds delay");
	} else
		lglprt(lgl, 1, "[limits] simplification not delayed since 'simpdelay' zero");
}

int
lglsat(LGL* lgl)
{
	int res;
	Lim lim;
	REQINITNOTFORKED();
	TRAPI("sat");
	lglstart(lgl, &lgl->times->all);
	lgl->stats->calls.sat++;
	ABORTIF(!lglmtstk(&lgl->clause), "clause terminating zero missing");
	lglfreezer(lgl);
	lglsetlim(lgl, &lim);
	res = lglisat(lgl, &lim, 0);
	lglstop(lgl);
	RETURN(lglsat, res);
	return res;
}

int
lglookahead(LGL* lgl)
{
	int ilit, res;
	REQINITNOTFORKED();
	TRAPI("lkhd");
	ABORTIF(!lglmtstk(&lgl->eassume), "imcompatible with 'lglassume'");
	ABORTIF(!lglmtstk(&lgl->clause), "clause terminating zero missing");
	ABORTIF(lgl->opts->druplig.val && lgl->opts->lkhd.val == 2,
			"can not use tree based look ahead while Druplig is enabled");
	lglstart(lgl, &lgl->times->all);
	lglstart(lgl, &lgl->times->lookahead);
	lgl->stats->calls.lkhd++;
	lglreset(lgl);
	assert(!lgl->lkhd);
	lgl->lkhd = 1;
	lglfreezer(lgl);
	if (lgl->level)
		lglbacktrack(lgl, 0);
	if (!lgl->mt && lglbcp(lgl)) {
		ilit = 0;
		if (lgl->opts->lkhd.val == 2 && !lglsmallirr(lgl))
			ilit = lgljwhlook(lgl);
		else
			switch (lgl->opts->lkhd.val) {
				case -1:
					ilit = lglocslook(lgl);
					break;
				case 0:
					ilit = lglislook(lgl);
					break;
				case 1:
					ilit = lgljwhlook(lgl);
					break;
				case 2:
					if (!lgltreelookaux(lgl, &ilit))
						assert(lgl->mt);
					break;
				case 3:
					assert(lgl->opts->lkhd.val == 3);
					ilit = lglsumlenlook(lgl);
					break;
				case 4:
				default:
					assert(lgl->opts->lkhd.val == 4);
					ilit = lglrelevancelook(lgl);
					break;
			}
		res = (!lgl->mt && ilit) ? lglexport(lgl, ilit) : 0;
		assert(!res || !lglelit2ext(lgl, res)->melted);
	} else
		lglmt(lgl), res = 0;
	assert(lgl->lkhd);
	lgl->lkhd = 0;
	lglstop(lgl);
	lglstop(lgl);
	TRANS(LOOKED);
	RETURN(lglookahead, res);
	return res;
}

int
lglchanged(LGL* lgl)
{
	int res;
	REQINITNOTFORKED();
	TRAPI("changed");
	REQUIRE(EXTENDED);
	res = lgl->changed;
	RETURN(lglchanged, res);
	return res;
}

int
lglsimp(LGL* lgl, int iterations)
{
	Lim lim;
	int res;
	REQINITNOTFORKED();
	TRAPI("simp %d", iterations);
	ABORTIF(iterations < 0, "negative number of simplification iterations");
	ABORTIF(!lglmtstk(&lgl->clause), "clause terminating zero missing");
	lglstart(lgl, &lgl->times->all);
	lgl->stats->calls.simp++;
	lglfreezer(lgl);
	CLR(lim);
	lim.decs = lgl->stats->decisions;
	res = lglisat(lgl, &lim, iterations);
	assert(!lgl->level);
	lglstop(lgl);
	RETURNARG(lglsimp, iterations, res);
	return res;
}

int
lglmaxvar(LGL* lgl)
{
	int res;
	REQINITNOTFORKED();
	TRAPI("maxvar");
	res = lgl->maxext;
	RETURN(lglmaxvar, res);
	return res;
}

int
lglincvar(LGL* lgl)
{
	int res;
	REQINITNOTFORKED();
	TRAPI("incvar");
	res = lgl->maxext + 1;
	(void)lglimport(lgl, res);
	RETURN(lglincvar, res);
	return res;
}

int
lglderef(LGL* lgl, int elit)
{
	int res;
	REQINIT();
	TRAPI("deref %d", elit);
	lgl->stats->calls.deref++;
	ABORTIF(!elit, "can not deref zero literal");
	REQUIRE(SATISFIED | EXTENDED);
	if (!(lgl->state & EXTENDED))
		lglextend(lgl);
	res = lglederef(lgl, elit);
	RETURNARG(lglderef, elit, res);
	return res;
}

int
lglfailed(LGL* lgl, int elit)
{
	unsigned bit;
	Ext* ext;
	int res;
	REQINITNOTFORKED();
	TRAPI("failed %d", elit);
	lgl->stats->calls.failed++;
	ABORTIF(!elit, "can not check zero failed literal");
	REQUIRE(UNSATISFIED | FAILED);
	ABORTIF(abs(elit) > lgl->maxext, "can not check unimported failed literal");
	ext = lglelit2ext(lgl, elit);
	bit = 1u << (elit < 0);
	ABORTIF(!(ext->assumed & bit), "can not check unassumed failed literal");
	if (!(lgl->state & FAILED)) {
		lglstart(lgl, &lgl->times->all);
		lglanafailed(lgl);
		lglstop(lgl);
	}
	res = (ext->failed & bit) != 0;
	RETURNARG(lglfailed, elit, res);
	return res;
}

int
lglinconsistent(LGL* lgl)
{
	int res;
	TRAPI("inconsistent");
	res = (lgl->mt != 0);
	RETURN(lglinconsistent, res);
	return res;
}

static int
lglefixed(LGL* lgl, int elit)
{
	int res, ilit;
	assert(elit);
	if (abs(elit) > lgl->maxext)
		return 0;
	ilit = lglimport(lgl, elit);
	if (!ilit)
		res = 0;
	else if (abs(ilit) == 1)
		res = ilit;
	else
		res = lglifixed(lgl, ilit);
	return res;
}

int
lglfixed(LGL* lgl, int elit)
{
	int res;
	REQINITNOTFORKED();
	TRAPI("fixed %d", elit);
	lgl->stats->calls.fixed++;
	ABORTIF(!elit, "can not deref zero literal");
	res = lglefixed(lgl, elit);
	RETURNARG(lglfixed, elit, res);
	return res;
}

int
lglrepr(LGL* lgl, int elit)
{
	int res, eidx = abs(elit);
	REQINITNOTFORKED();
	TRAPI("repr %d", elit);
	lgl->stats->calls.repr++;
	if (eidx > lgl->maxext)
		res = elit;
	else
		res = lglerepr(lgl, elit);
	RETURNARG(lglrepr, elit, res);
	return res;
}

void
lglfreeze(LGL* lgl, int elit)
{
	Ext* ext;
	REQINITNOTFORKED();
	TRAPI("freeze %d", elit);
	lgl->stats->calls.freeze++;
	ABORTIF(!elit, "can not freeze zero literal");
	REQUIRE(UNUSED | OPTSET | USED | RESET | SATISFIED | UNSATISFIED | FAILED | LOOKED | UNKNOWN | EXTENDED);
	LOG(2, "freezing external literal %d", elit);
	(void)lglimport(lgl, elit);
	ext = lglelit2ext(lgl, elit);
	ABORTIF(ext->melted, "freezing melted literal %d", elit);
	assert(!ext->blocking && !ext->eliminated);
	ABORTIF(ext->frozen == INT_MAX, "literal %d frozen too often", elit);
	ext->frozen++;
	if (!ext->frozen) {
		lgl->stats->irrprgss++;
		lglmelter(lgl);
	}
	if (lgl->clone)
		lglfreeze(lgl->clone, elit);
}

int
lglfrozen(LGL* lgl, int elit)
{
	Ext* ext;
	int res;
	REQINITNOTFORKED();
	TRAPI("frozen %d", elit);
	ABORTIF(!elit, "can not check zero literal for being frozen");
	if (abs(elit) > lgl->maxext)
		res = INT_MAX;
	else if (!(ext = lglelit2ext(lgl, elit))->imported)
		res = INT_MAX;
	else
		res = ext->frozen;
	RETURNARG(lglfrozen, elit, res);
	return res;
}

int
lglusable(LGL* lgl, int elit)
{
	Ext* ext;
	int res;
	REQINITNOTFORKED();
	TRAPI("usable %d", elit);
	ABORTIF(!elit, "can not check zero literal for being usable");
	if (abs(elit) > lgl->maxext)
		res = 1;
	else if (!(ext = lglelit2ext(lgl, elit))->imported)
		res = 1;
	else
		res = !ext->melted;
	RETURNARG(lglusable, elit, res);
	return res;
}

static int
lglereusable(LGL* lgl, int elit)
{
	Ext* ext;
	int res;
	if (abs(elit) > lgl->maxext)
		res = 1;
	else if (!(ext = lglelit2ext(lgl, elit))->imported)
		res = 1;
	else if (ext->blocking)
		res = 0;
	else if (ext->eliminated)
		res = 0;
	else if (ext->equiv)
		res = 0;
	else if (abs(ext->repr) == 1)
		res = 0;
	else
		res = 1;
	return res;
}

int
lglreusable(LGL* lgl, int elit)
{
	int res;
	REQINITNOTFORKED();
	TRAPI("reusable %d", elit);
	ABORTIF(!elit, "can not check zero literal for being reusable");
	res = lglereusable(lgl, elit);
	RETURNARG(lglreusable, elit, res);
	return res;
}

void
lglreuse(LGL* lgl, int elit)
{
	Ext* ext;
	REQINITNOTFORKED();
	TRAPI("reuse %d", elit);
	ABORTIF(!elit, "can not reuse zero literal");
	ABORTIF(!lglereusable(lgl, elit), "can not reuse non-reusable literal");
	if (abs(elit) <= lgl->maxext) {
		ext = lglelit2ext(lgl, elit);
		if (ext->imported) {
			assert(!ext->blocking);
			assert(!ext->eliminated);
			if (ext->melted) {
				LOG(2, "forced permanently melted external %d to be reused", elit);
				ext->melted = 0;
				lglmelter(lgl);
			}
		}
	}
	if (lgl->clone)
		lglreuse(lgl->clone, elit);
}

void
lglmeltall(LGL* lgl)
{
	int idx, melted;
	Ext* ext;
	REQINITNOTFORKED();
	TRAPI("meltall");
	melted = 0;
	for (idx = 1; idx <= lgl->maxext; idx++) {
		ext = lglelit2ext(lgl, idx);
		ext->melted = 0;
		if (!ext->frozen)
			continue;
		lgl->stats->irrprgss++;
		ext->frozen = 0;
		melted++;
	}
	lglprt(lgl, 1, "[meltall] melted %d frozen literals", melted);
	lglmelter(lgl);
	if (lgl->clone)
		lglmeltall(lgl->clone);
}

void
lglmelt(LGL* lgl, int elit)
{
	Ext* ext;
	REQINITNOTFORKED();
	TRAPI("melt %d", elit);
	lgl->stats->calls.melt++;
	ABORTIF(!elit, "can not melt zero literal");
	REQUIRE(UNUSED | OPTSET | USED | RESET | SATISFIED | UNSATISFIED | FAILED | UNKNOWN | LOOKED | EXTENDED);
	LOG(2, "melting external literal %d", elit);
	(void)lglimport(lgl, elit);
	ext = lglelit2ext(lgl, elit);
	ABORTIF(!ext->frozen, "can not melt fully unfrozen literal %d", elit);
	assert(!ext->blocking && !ext->eliminated);
	ext->frozen--;
	lglmelter(lgl); // TODO guard  by !ext->frozen?
	if (lgl->clone)
		lglmelt(lgl->clone, elit);
}

void
lglreconstk(LGL* lgl, int** startptr, int** toptr)
{
	REQINITNOTFORKED();
	TRAPI("reconstk");
	lglfitstk(lgl, &lgl->extend); // 'lglcompact' -> 'lglclone'!!
	if (startptr)
		*startptr = lgl->extend.start;
	if (toptr)
		*toptr = lgl->extend.top;
	if (lgl->clone)
		lglreconstk(lgl->clone, 0, 0);
}

static void
lglprstart(LGL* lgl)
{
	assert(lgl->prefix);
	fputs(lgl->prefix, lgl->out);
	if (lgl->tid >= 0)
		fprintf(lgl->out, "%d ", lgl->tid);
}

static void
lglprs(LGL* lgl, const char* fmt, ...)
{
	va_list ap;
	lglprstart(lgl);
	va_start(ap, fmt);
	vfprintf(lgl->out, fmt, ap);
	va_end(ap);
	fputc('\n', lgl->out);
}

static void
lglprsline(LGL* lgl)
{
	lglprstart(lgl);
	fputc('\n', lgl->out);
}

static double
lglsqr(double a)
{
	return a * a;
}

static void
lglgluestats(LGL* lgl)
{
	int64_t added, reduced, retired, forcing, resolved, conflicts;
	int64_t wadded, wreduced, wretired, wforcing, wresolved, wconflicts;
	int64_t avgadded, avgreduced, avgretired, avgforcing, avgresolved, avgconflicts;
	double madded, mreduced, mretired, mforcing, mresolved, mconflicts;
	double vadded, vreduced, vretired, vforcing, vresolved, vconflicts;
	double sadded, sreduced, sretired, sforcing, sresolved, sconflicts;
	Stats* s = lgl->stats;
	int glue, maxglue;
	lglprs(lgl,
		   "scaled glue%11s %3s %9s %3s %9s %3s %9s %3s %9s %3s %9s %7s",
		   "added",
		   "",
		   "reduced",
		   "",
		   "retired",
		   "",
		   "forcing",
		   "",
		   "resolved",
		   "",
		   "conflicts",
		   "maxmb");
	added = reduced = retired = forcing = resolved = conflicts = 0;
	wadded = wreduced = wretired = wforcing = wresolved = wconflicts = 0;
	for (glue = 0; glue <= MAXGLUE; glue++) {
		added += s->lir[glue].added;
		reduced += s->lir[glue].reduced;
		retired += s->lir[glue].retired;
		forcing += s->lir[glue].forcing;
		resolved += s->lir[glue].resolved;
		conflicts += s->lir[glue].conflicts;
		wadded += glue * s->lir[glue].added;
		wreduced += glue * s->lir[glue].reduced;
		wretired += glue * s->lir[glue].retired;
		wforcing += glue * s->lir[glue].forcing;
		wresolved += glue * s->lir[glue].resolved;
		wconflicts += glue * s->lir[glue].conflicts;
	}
	avgadded = added ? (((10 * wadded) / added + 5) / 10) : 0;
	avgreduced = reduced ? (((10 * wreduced) / reduced + 5) / 10) : 0;
	avgretired = retired ? (((10 * wretired) / retired + 5) / 10) : 0;
	avgforcing = forcing ? (((10 * wforcing) / forcing + 5) / 10) : 0;
	avgresolved = resolved ? (((10 * wresolved) / resolved + 5) / 10) : 0;
	avgconflicts = conflicts ? (((10 * wconflicts) / conflicts + 5) / 10) : 0;
	lglprsline(lgl);
	lglprs(lgl,
		   "all %14lld %13lld %13lld %13lld %13lld %13lld",
		   (LGLL)added,
		   (LGLL)reduced,
		   (LGLL)retired,
		   (LGLL)forcing,
		   (LGLL)resolved,
		   (LGLL)conflicts);
	lglprsline(lgl);
	if (lgl->opts->verbose.val < 2) {
		for (maxglue = MAXGLUE; maxglue >= 1; maxglue--)
			if (s->lir[maxglue].added)
				break;
		if (maxglue + 1 == MAXGLUE)
			maxglue = MAXGLUE;
	} else
		maxglue = MAXGLUE;
	for (glue = 0; glue <= maxglue; glue++) {
		lglprs(lgl,
			   "%2d %-5d %9lld %3.0f%c%9lld %3.0f%c%9lld %3.0f%c%9lld %3.0f%c%9lld %3.0f%c%9lld %3.0f%c %6.1f",
			   glue,
			   lglunscaleglue(lgl, glue),
			   (LGLL)s->lir[glue].added,
			   lglpcnt(s->lir[glue].added, added),
			   (glue == avgadded) ? '<' : ' ',
			   (LGLL)s->lir[glue].reduced,
			   lglpcnt(s->lir[glue].reduced, reduced),
			   (glue == avgreduced) ? '<' : ' ',
			   (LGLL)s->lir[glue].retired,
			   lglpcnt(s->lir[glue].retired, retired),
			   (glue == avgretired) ? '<' : ' ',
			   (LGLL)s->lir[glue].forcing,
			   lglpcnt(s->lir[glue].forcing, forcing),
			   (glue == avgforcing) ? '<' : ' ',
			   (LGLL)s->lir[glue].resolved,
			   lglpcnt(s->lir[glue].resolved, resolved),
			   (glue == avgresolved) ? '<' : ' ',
			   (LGLL)s->lir[glue].conflicts,
			   lglpcnt(s->lir[glue].conflicts, conflicts),
			   (glue == avgconflicts) ? '<' : ' ',
			   s->lir[glue].maxbytes / (double)(1 << 20));
	}
	lglprsline(lgl);

	madded = lglavg(wadded, added), mreduced = lglavg(wreduced, reduced), mretired = lglavg(wretired, retired),
	mforcing = lglavg(wforcing, forcing), mresolved = lglavg(wresolved, resolved),
	mconflicts = lglavg(wconflicts, conflicts);

	lglprs(
		lgl, "avg  %19.1f%14.1f%14.1f%14.1f%14.1f%14.1f", madded, mreduced, mretired, mforcing, mresolved, mconflicts);

	vadded = vreduced = vretired = vforcing = vresolved = vconflicts = 0;
	for (glue = 0; glue <= MAXGLUE; glue++) {
		vadded += s->lir[glue].added * lglsqr(glue - madded);
		vreduced += s->lir[glue].reduced * lglsqr(glue - mreduced);
		vretired += s->lir[glue].retired * lglsqr(glue - mretired);
		vforcing += s->lir[glue].forcing * lglsqr(glue - mforcing);
		vresolved += s->lir[glue].resolved * lglsqr(glue - mresolved);
		vconflicts += s->lir[glue].conflicts * lglsqr(glue - mconflicts);
	}
	sadded = sqrt(lglavg(vadded, added));
	sreduced = sqrt(lglavg(vreduced, reduced));
	sretired = sqrt(lglavg(vretired, retired));
	sforcing = sqrt(lglavg(vforcing, forcing));
	sresolved = sqrt(lglavg(vresolved, resolved));
	sconflicts = sqrt(lglavg(vconflicts, conflicts));

	lglprs(
		lgl, "std  %19.1f%14.1f%14.1f%14.1f%14.1f%14.1f", sadded, sreduced, sretired, sforcing, sresolved, sconflicts);
}

typedef struct TN
{
	double t;
	const char* n;
} TN;

static int
lglcmptn(const TN* a, const TN* b)
{
	if (a->t > b->t)
		return -1;
	if (a->t < b->t)
		return 1;
	return strcmp(a->n, b->n);
}

#define INSTN(NAME, FIELD)                                                                                             \
	do {                                                                                                               \
		TN* tn;                                                                                                        \
		if (lglignprofptr(lgl, &ts->FIELD))                                                                            \
			break;                                                                                                     \
		tn = tns + ntns++;                                                                                             \
		ASSERT(ntns <= sztns);                                                                                         \
		tn->t = ts->FIELD;                                                                                             \
		tn->n = #NAME;                                                                                                 \
	} while (0)

static void
lglprofsort(LGL* lgl)
{
	int i, ntns, nimportant, nprint;
	Times* ts = lgl->times;
	const int sztns = 100;
	TN tns[sztns];
	double sum;

	ntns = 0;

	INSTN(analysis, analysis);
	INSTN(backward, backward);
	INSTN(bca, bca);
	INSTN(block, block);
	INSTN(bump, bump);
	INSTN(card, card);
	INSTN(cce, cce);
	INSTN(decide, decide);
	INSTN(decompose, decompose);
	INSTN(defrag, defrag);
	INSTN(druplig, druplig);
	INSTN(elim, elim);
	INSTN(gauss, gauss);
	INSTN(gc, gc);
	INSTN(heapdecision, heapdecision);
	INSTN(locs, locs);
	INSTN(mincls, mincls);
	INSTN(phase, phase);
	INSTN(probe, probe);
	INSTN(prune, prune);
	INSTN(quatres1, quatres1);
	INSTN(quatres2, quatres2);
	INSTN(quatres, quatres);
	INSTN(queuedecision, queuedecision);
	INSTN(queuesort, queuesort);
	INSTN(redcls, redcls);
	INSTN(reduce, reduce);
	INSTN(restart, restart);
	INSTN(showfeatures, showfeatures);
	INSTN(showscoredist, showscoredist);
	INSTN(subl, subl);
	INSTN(sweep, sweep);
	INSTN(ternres, ternres);
	INSTN(transred, transred);
	INSTN(unhide, unhide);

	// Force insertion sort to avoid implicit memory allocation!
	// Otherwise an already existing memory corruption will trigger
	// another signal and thus might result in non-termination.

	ISORT(TN, lglcmptn, tns, ntns);

	sum = 0;
	assert(ntns > 3);
	for (nimportant = ntns - 1; nimportant > 3; nimportant--) {
		sum += tns[nimportant].t;
		if (lglpcnt(sum, ts->all) >= 1.0)
			break;
	}
	if (nimportant == ntns - 1)
		nimportant++;

	if (!lgl->opts->profilelong.val && lgl->opts->verbose.val < 2)
		nprint = nimportant;
	else
		nprint = ntns;

	for (i = 0; i < nprint; i++) {
		if (i == nimportant) {
			if (i)
				lglprs(lgl, "");
			lglprs(lgl, "%8.3f < 1%% rest (below)", sum);
			if (i + 1 < nprint)
				lglprs(lgl, "");
		}
		lglprs(lgl, "%8.3f %3.0f%% %s", tns[i].t, lglpcnt(tns[i].t, ts->all), tns[i].n);
	}

	if (nimportant == nprint && nprint < ntns) {
		if (nprint)
			lglprs(lgl, "");
		lglprs(lgl, "%8.3f < 1%% rest (not shown)", sum);
	}
}

static void
lglprof(LGL* lgl)
{
	Times* ts = lgl->times;
	double t = ts->all, simp, search;

	assert(lgl->opts->verbose.val >= 0);
	simp = ts->preprocessing + ts->inprocessing;
	if (!lgl->opts->verbose.val || !lgl->opts->profile.val)
		goto SHORT;

	lglprofsort(lgl);

	if (!lglignprofptr(lgl, &ts->prb.simple) && (lgl->opts->profilelong.val || lgl->opts->verbose.val >= 2 ||
												 ts->prb.simple || ts->prb.basic || ts->prb.treelook)) {
		lglprs(lgl, "----------------------------------");
		lglprs(lgl,
			   "%8.3f %3.0f%% probe simple    %3.0f%%",
			   ts->prb.simple,
			   lglpcnt(ts->prb.simple, t),
			   lglpcnt(ts->prb.simple, ts->probe));
		lglprs(lgl,
			   "%8.3f %3.0f%% probe basic     %3.0f%%",
			   ts->prb.basic,
			   lglpcnt(ts->prb.basic, t),
			   lglpcnt(ts->prb.basic, ts->probe));
		lglprs(lgl,
			   "%8.3f %3.0f%% probe tree-look %3.0f%%",
			   ts->prb.treelook,
			   lglpcnt(ts->prb.treelook, t),
			   lglpcnt(ts->prb.treelook, ts->probe));
	}
	lglprs(lgl, "==================================");
	lglprs(lgl,
		   "%8.3f %3.0f%% preprocessing   %3.0f%%",
		   ts->preprocessing,
		   lglpcnt(ts->preprocessing, t),
		   lglpcnt(ts->preprocessing, simp));
	lglprs(lgl,
		   "%8.3f %3.0f%% inprocessing    %3.0f%%",
		   ts->inprocessing,
		   lglpcnt(ts->inprocessing, t),
		   lglpcnt(ts->inprocessing, simp));
	lglprs(lgl, "==================================");
SHORT:
	lglprs(lgl, "%8.3f %3.0f%% simplifying", simp, lglpcnt(simp, t));
	if (lgl->stats->calls.lkhd)
		lglprs(lgl, "%8.3f %3.0f%% lookahead", ts->lookahead, lglpcnt(ts->lookahead, t));
	search = ts->search - ts->inprocessing;
	lglprs(lgl, "%8.3f %3.0f%% search", search, lglpcnt(search, t));
	lglprs(lgl, "==================================");
	lglprs(lgl, "%8.3f %3.0f%% all", t, 100.0);
}

void
lglstats(LGL* lgl)
{
	long long visits, min, p, steps, sum;
	int remaining, removed;
	Stats* s;
	double t;
	REQINITNOTFORKED();
	if (lgl->opts->verbose.val < 0)
		return;
	lglflushtimers(lgl);
	s = lgl->stats;
	t = lgl->times->all;
	p = s->props.search + s->props.simp + s->props.lkhd;
	if (!lgl->opts->verbose.val)
		goto SHORT;

	lglprs(lgl, "agil: %.2f%% agility", 100.0 * (s->agility / (double)(1ll << 32)));

	sum = s->prb.basic.ate.trnr + s->prb.basic.ate.lrg;
	lglprs(lgl,
		   "bate: %lld basic ate = %d trn %.0f%% + %d lrg %.0f%% in %d updates",
		   sum,
		   s->prb.basic.ate.trnr,
		   lglpcnt(s->prb.basic.ate.trnr, sum),
		   s->prb.basic.ate.lrg,
		   lglpcnt(s->prb.basic.ate.lrg, sum),
		   s->prb.basic.ate.count);

	lglprs(lgl,
		   "bcas: %d count, %lld added (%lld skipped), %lld steps",
		   s->bca.count,
		   (LGLL)s->bca.added,
		   s->bca.skipped,
		   (LGLL)s->bca.steps);

	lglprs(lgl,
		   "bkwd: tried %lld clauses, %lld lits, %.2f lits/clause",
		   (LGLL)s->bkwd.tried.clauses,
		   (LGLL)s->bkwd.tried.lits,
		   lglavg(s->bkwd.tried.lits, s->bkwd.tried.clauses));
	lglprs(lgl,
		   "bkwd: checked %lld occs, %.2f occs/clause, %.2f occs/lit",
		   (LGLL)s->bkwd.tried.occs,
		   lglavg(s->bkwd.tried.occs, s->bkwd.tried.clauses),
		   lglavg(s->bkwd.tried.occs, s->bkwd.tried.lits));
	lglprs(lgl, "bkwd: subsumed %d binary, %d ternary, %d large", s->bkwd.sub2, s->bkwd.sub3, s->bkwd.subl);
	lglprs(lgl,
		   "bkwd: strengthened %d binary, "
		   "%d ternary (%.0f%% self), %d large (%.0f%% self)",
		   s->bkwd.str2,
		   s->bkwd.str3,
		   lglpcnt(s->bkwd.str3self, s->bkwd.str3),
		   s->bkwd.strl,
		   lglpcnt(s->bkwd.strlself, s->bkwd.strl));

	lglprs(lgl,
		   "blkd: %d bces, %d removed, %lld resolutions, %lld steps",
		   s->blk.count,
		   s->blk.clauses,
		   (LGLL)s->blk.res,
		   (LGLL)s->blk.steps);
	lglprs(lgl,
		   "blkd: %d blocking literals %.0f%%, %d pure",
		   s->blk.lits,
		   lglpcnt(s->blk.lits, 2 * lgl->maxext),
		   s->blk.pure);

	lglprs(lgl,
		   "bump: %lld bumped seen, %lld lits %.1f on average",
		   s->bump.count,
		   (LGLL)s->bump.lits,
		   lglavg(s->bump.lits, s->bump.count));

	lglprs(lgl,
		   "card: %d count, %d units, %d am1, %lld steps",
		   s->card.count,
		   s->card.units,
		   s->card.expam1,
		   (LGLL)s->card.steps);
	lglprs(lgl,
		   "card: %d rescheduled, %lld eliminated, %lld resolved, %lld subsumed",
		   s->card.resched,
		   (LGLL)s->card.eliminated,
		   (LGLL)s->card.resolved,
		   (LGLL)s->card.subsumed);
	lglprs(lgl,
		   "card: %d at-most-one constraints found, average size %.2f",
		   s->card.found.am1.total.cnt,
		   lglavg(s->card.found.am1.total.sum, s->card.found.am1.total.cnt));
	lglprs(lgl,
		   "card: %d at-most-one constraints used %.0f%%, average size %.2f",
		   s->card.used.am1.cnt,
		   lglpcnt(s->card.used.am1.cnt, s->card.found.am1.total.cnt),
		   lglavg(s->card.used.am1.sum, s->card.used.am1.cnt));
	lglprs(lgl,
		   "card: %d at-most-two constraints found, average size %.2f",
		   s->card.found.am2.total.cnt,
		   lglavg(s->card.found.am2.total.sum, s->card.found.am2.total.cnt));
	lglprs(lgl,
		   "card: %d at-most-two constraints used %.0f%%, average size %.2f",
		   s->card.used.am2.cnt,
		   lglpcnt(s->card.used.am2.cnt, s->card.found.am2.total.cnt),
		   lglavg(s->card.used.am2.sum, s->card.used.am2.cnt));

	lglprs(lgl,
		   "cces: %d cces, %d eliminated, %d ate %.0f%%, %d abce %.0f%%",
		   s->cce.count,
		   s->cce.eliminated,
		   s->cce.ate,
		   lglpcnt(s->cce.ate, s->cce.eliminated),
		   s->cce.abce,
		   lglpcnt(s->cce.abce, s->cce.eliminated));
	lglprs(lgl, "cces: %lld probed, %d lifted, %d failed", (LGLL)s->cce.probed, s->cce.lifted, s->cce.failed);
	lglprs(lgl,
		   "cces: %lld cached, %lld rsz, %lld cols %.0f%%, "
		   "%lld srch, %lld hits %.0f%%",
		   (LGLL)s->cce.cache.ins,
		   (LGLL)s->cce.cache.rsz,
		   (LGLL)s->cce.cache.cols,
		   lglpcnt(s->cce.cache.cols, s->cce.cache.ins),
		   (LGLL)s->cce.cache.search,
		   (LGLL)s->cce.cache.hits,
		   lglpcnt(s->cce.cache.hits, s->cce.cache.search));

	lglprs(lgl,
		   "clls: %lld sat, %lld simp, %lld freeze, %lld melt",
		   (LGLL)s->calls.sat,
		   (LGLL)s->calls.simp,
		   (LGLL)s->calls.freeze,
		   (LGLL)s->calls.melt);
	lglprs(lgl,
		   "clls: %lld add, %lld assume, %lld deref, %lld failed",
		   (LGLL)s->calls.add,
		   (LGLL)s->calls.assume,
		   (LGLL)s->calls.deref,
		   (LGLL)s->calls.failed);
	lglprs(lgl,
		   "clls: %lld lkhd, %lld cassume, %lld mosat",
		   (LGLL)s->calls.lkhd,
		   (LGLL)s->calls.cassume,
		   (LGLL)s->calls.mosat);

	lglprs(lgl, "coll: %d gcs, %d rescored clauses, %d rescored vars", s->gcs, s->rescored.clauses, s->rescored.vars);

	lglprs(lgl,
		   "dcps: %d decompositions, %d equivalent %.0f%%",
		   s->decomps,
		   s->equiv.sum,
		   lglpcnt(s->equiv.sum, lgl->maxext));

	lglprs(lgl,
		   "deco: %lld decision-only clauses %.0f%%, %.0f%% reduction",
		   s->mincls.deco,
		   lglpcnt(s->mincls.deco, s->clauses.learned),
		   lglpcnt(s->deco.sum.orig - s->deco.sum.red, s->deco.sum.orig));

	lglprs(lgl,
		   "decs: %lld total, %lld rnd %.3f%%, %lld heap %.3f%, %lld queue %.3f%%",
		   (LGLL)s->decisions,
		   (LGLL)s->randecs,
		   lglpcnt(s->randecs, s->decisions),
		   (LGLL)s->hdecs,
		   lglpcnt(s->hdecs, s->decisions),
		   (LGLL)s->qdecs,
		   lglpcnt(s->qdecs, s->decisions));

	lglprs(lgl,
		   "elms: %d elims, %d eliminated %.0f%%, %d pure %.0f%%",
		   s->elm.count,
		   s->elm.elmd,
		   lglpcnt(s->elm.elmd, lgl->maxext),
		   s->elm.pure,
		   lglpcnt(s->elm.pure, s->elm.elmd));
	lglprs(lgl, "elms: %d rounds, %.1f rounds/bve", s->elm.rounds, lglavg(s->elm.rounds, s->elm.count));
	lglprs(lgl,
		   "elms: %d small %.0f%%, %d large %.0f%%",
		   s->elm.small.elm,
		   lglpcnt(s->elm.small.elm, s->elm.elmd),
		   s->elm.large,
		   lglpcnt(s->elm.large, s->elm.elmd));
	lglprs(lgl,
		   "elms: %d tried small, %d succeeded %.0f%%, %d failed %.0f%%",
		   s->elm.small.tried,
		   s->elm.small.tried - s->elm.small.failed,
		   lglpcnt(s->elm.small.tried - s->elm.small.failed, s->elm.small.tried),
		   s->elm.small.failed,
		   lglpcnt(s->elm.small.failed, s->elm.small.tried));
	lglprs(lgl, "elms: %d subsumed, %d strengthened, %d blocked", s->elm.sub, s->elm.str, s->elm.blkd);
	lglprs(lgl,
		   "elms: %lld copies, %lld resolutions, %lld ipos",
		   (LGLL)s->elm.copies,
		   (LGLL)s->elm.resolutions,
		   (LGLL)s->elm.ipos);
	lglprs(lgl, "elms: %lld subchks, %lld strchks", (LGLL)s->elm.subchks, (LGLL)s->elm.strchks);

	lglprs(lgl,
		   "gaus: %d extractions, %lld extracted, %.1f size, %d max",
		   s->gauss.count,
		   s->gauss.extracted.total,
		   lglavg(s->gauss.arity.sum.total, s->gauss.extracted.total),
		   s->gauss.arity.max);
	lglprs(lgl,
		   "gaus: %lld exactly-1 constraints of average arity %.1f",
		   s->gauss.extracted.exactly1,
		   lglavg(s->gauss.arity.sum.exactly1, s->gauss.extracted.exactly1));
	lglprs(lgl,
		   "gaus: exported %d units, %d binary and %d ternary equations",
		   s->gauss.units,
		   s->gauss.equivs,
		   s->gauss.trneqs);
	steps = s->gauss.steps.extr + s->gauss.steps.extr;
	lglprs(lgl,
		   "gaus: %d gc, %lld steps, %lld extr %.0f%%, %lld elim %.0f%%",
		   s->gauss.gcs,
		   (LGLL)steps,
		   s->gauss.steps.extr,
		   lglpcnt(s->gauss.steps.extr, steps),
		   s->gauss.steps.elim,
		   lglpcnt(s->gauss.steps.elim, steps));

	lglprs(lgl,
		   "glue: %.1f orig avg, %.1f real avg, %.1f scaled avg",
		   lglavg(s->clauses.glue, s->clauses.learned),
		   lglavg(s->clauses.realglue, s->clauses.learned),
		   lglavg(s->clauses.scglue, s->clauses.learned));
	lglprs(lgl,
		   "glue: %lld maxscaledglue=%d (%.0f%%), %lld kept %.0f%%",
		   (LGLL)s->clauses.maxglue.count,
		   MAXGLUE,
		   lglpcnt(s->clauses.maxglue.count, s->clauses.learned),
		   (LGLL)s->clauses.maxglue.kept,
		   lglpcnt(s->clauses.maxglue.kept, s->clauses.learned));

	lglprs(lgl,
		   "hbrs: %d = %d simple %.0f%% + %d trn %.0f%% + %d lrg %.0f%%, %d sub %.0f%%",
		   s->hbr.cnt,
		   s->hbr.simple,
		   lglpcnt(s->hbr.simple, s->hbr.cnt),
		   s->hbr.trn,
		   lglpcnt(s->hbr.trn, s->hbr.cnt),
		   s->hbr.lrg,
		   lglpcnt(s->hbr.lrg, s->hbr.cnt),
		   s->hbr.sub,
		   lglpcnt(s->hbr.sub, s->hbr.cnt));

	lglprs(lgl,
		   "locs: %d count, %lld flips, %lld mems, %d minimun",
		   s->locs.count,
		   (LGLL)s->locs.flips,
		   (LGLL)s->locs.mems,
		   s->locs.min);

	assert(s->lits.nonmin >= s->lits.learned);
	min = s->lits.nonmin - s->lits.learned;
	lglprs(
		lgl, "lrnd: %lld clauses, %.1f length", (LGLL)s->clauses.learned, lglavg(s->lits.learned, s->clauses.learned));
	lglprs(lgl,
		   "lrnd: %lld lits = %lld orig - %lld removed %.0f%%",
		   (LGLL)s->lits.learned,
		   (LGLL)s->lits.nonmin,
		   (LGLL)min,
		   lglpcnt(min, s->lits.nonmin));
	lglprs(lgl,
		   "lrnd: %lld uips %.0f%%, %lld flips %.0f%%",
		   (LGLL)s->uips,
		   lglpcnt(s->uips, s->clauses.learned),
		   (LGLL)s->decflipped,
		   lglpcnt(s->decflipped, s->clauses.learned));

	sum = s->mincls.min + s->mincls.bin + s->mincls.size + s->mincls.deco;
	lglprs(lgl,
		   "mins: %lld mins %.0f%%, %lld bin %.0f%%, %lld size %.0f%%",
		   (LGLL)s->mincls.min,
		   lglpcnt(s->mincls.min, sum),
		   (LGLL)s->mincls.bin,
		   lglpcnt(s->mincls.bin, sum),
		   (LGLL)s->mincls.size,
		   lglpcnt(s->mincls.size, sum),
		   (LGLL)s->mincls.deco,
		   lglpcnt(s->mincls.deco, sum));
	lglprs(lgl,
		   "mins: %lld poisoned searches, %lld hits, %.0f%% hit rate",
		   (LGLL)s->mincls.poison.search,
		   (LGLL)s->mincls.poison.hits,
		   lglpcnt(s->mincls.poison.hits, s->mincls.poison.search));
	lglprs(lgl,
		   "mins: %lld used twice searches, %lld hits, %.0f%% hit rate",
		   (LGLL)s->mincls.usedtwice.search,
		   (LGLL)s->mincls.usedtwice.hits,
		   lglpcnt(s->mincls.usedtwice.hits, s->mincls.usedtwice.search));

	sum = s->moved.bin + s->moved.trn;
	lglprs(lgl,
		   "move: moved %lld, %lld bin %.0f%%, %lld trn %.0f%%",
		   sum,
		   (LGLL)s->moved.bin,
		   lglpcnt(s->moved.bin, sum),
		   (LGLL)s->moved.trn,
		   lglpcnt(s->moved.trn, sum));

	sum = s->otfs.str.red + s->otfs.str.irr;
	assert(sum == s->otfs.total);
	assert(sum == s->otfs.str.bin + s->otfs.str.trn + s->otfs.str.lrg);
	lglprs(lgl,
		   "otfs: %lld driving %.0f%%, %lld restarting %.0f%%",
		   (LGLL)s->otfs.driving,
		   lglpcnt(s->otfs.driving, sum),
		   (LGLL)s->otfs.restarting,
		   lglpcnt(s->otfs.restarting, sum));
	lglprs(lgl,
		   "otfs: str %lld, %lld red %.0f%%, %lld irr %.0f%%",
		   sum,
		   (LGLL)s->otfs.str.red,
		   lglpcnt(s->otfs.str.red, sum),
		   (LGLL)s->otfs.str.irr,
		   lglpcnt(s->otfs.str.irr, sum));
	lglprs(lgl,
		   "otfs: str %lld, %lld bin %.0f%%, %lld trn %.0f%%, %lld lrg %.0f%%",
		   sum,
		   (LGLL)s->otfs.str.bin,
		   lglpcnt(s->otfs.str.bin, sum),
		   (LGLL)s->otfs.str.trn,
		   lglpcnt(s->otfs.str.trn, sum),
		   (LGLL)s->otfs.str.lrg,
		   lglpcnt(s->otfs.str.lrg, sum));

	sum = s->otfs.sub.red + s->otfs.sub.irr;
	assert(sum == s->otfs.sub.bin + s->otfs.sub.trn + s->otfs.sub.lrg);
	lglprs(lgl,
		   "otfs: sub %lld, %lld red %.0f%%, %lld irr %.0f%%",
		   sum,
		   (LGLL)s->otfs.sub.red,
		   lglpcnt(s->otfs.sub.red, sum),
		   (LGLL)s->otfs.sub.irr,
		   lglpcnt(s->otfs.sub.irr, sum));
	lglprs(lgl,
		   "otfs: sub %lld, %lld bin %.0f%%, %lld trn %.0f%%, %lld lrg %.0f%%",
		   sum,
		   (LGLL)s->otfs.sub.bin,
		   lglpcnt(s->otfs.sub.bin, sum),
		   (LGLL)s->otfs.sub.trn,
		   lglpcnt(s->otfs.sub.trn, sum),
		   (LGLL)s->otfs.sub.lrg,
		   lglpcnt(s->otfs.sub.lrg, sum));

	lglprs(lgl,
		   "phas: %lld computed, %lld set, %lld pos (%.0f%%), %lld neg (%.0f%%)",
		   (LGLL)s->phase.count,
		   (LGLL)s->phase.set,
		   (LGLL)s->phase.pos,
		   lglpcnt(s->phase.pos, s->phase.set),
		   (LGLL)s->phase.neg,
		   lglpcnt(s->phase.neg, s->phase.set));
	lglprs(lgl,
		   "phas: %lld flipped decision phases randomly %.0f%%",
		   (LGLL)s->randphases,
		   lglpcnt(s->randphases, s->decisions));

	lglprs(lgl,
		   "prbs: %d basic, %lld probed, %d failed, %d lifted",
		   s->prb.basic.count,
		   (LGLL)s->prb.basic.probed,
		   s->prb.basic.failed,
		   s->prb.basic.lifted);
	lglprs(lgl,
		   "prbs: %d simple, %lld probed, %d failed, %d eqs",
		   s->prb.simple.count,
		   (LGLL)s->prb.simple.probed,
		   s->prb.simple.failed,
		   s->prb.simple.eqs);
	lglprs(lgl,
		   "prbs: %d treelook, %lld probed, %d failed, %d lifted",
		   s->prb.treelook.count,
		   (LGLL)s->prb.treelook.probed,
		   s->prb.treelook.failed,
		   s->prb.treelook.lifted);

	lglprs(lgl,
		   "prom: %lld locked %.0f%% out of %lld tried",
		   (LGLL)s->promote.locked,
		   lglpcnt(s->promote.locked, s->promote.tried),
		   (LGLL)s->promote.tried);

	lglprs(lgl, "prps: %lld props, %.0f props/dec", (LGLL)p, lglavg(s->props.search, s->decisions));
	lglprs(lgl,
		   "prps: %.0f%% srch, %.0f%% simp, %.0f%% lkhd",
		   lglpcnt(s->props.search, p),
		   lglpcnt(s->props.simp, p),
		   lglpcnt(s->props.lkhd, p));

	lglprs(lgl,
		   "prun: %lld decisions, %lld pure %.2f%%",
		   (LGLL)s->prune.decs,
		   (LGLL)s->prune.pure,
		   lglpcnt(s->prune.pure, s->prune.decs));
	lglprs(lgl,
		   "prun: %lld calls, %.1f%% per decision on level %.3f",
		   (LGLL)s->prune.calls,
		   lglavg(s->prune.calls, s->prune.decs),
		   lglavg(s->prune.levels, s->prune.calls));
	lglprs(lgl,
		   "prun: "
		   "%lld sat %.0f%%, %lld unsat %.0f%%, %lld lim %.0f%%",
		   (LGLL)s->prune.sat,
		   lglpcnt(s->prune.sat, s->prune.calls),
		   (LGLL)s->prune.uns,
		   lglpcnt(s->prune.uns, s->prune.calls),
		   (LGLL)s->prune.lim,
		   lglpcnt(s->prune.lim, s->prune.calls));
	lglprs(lgl,
		   "prun: %lld child conflicts, %lld decisions, %lld propagations",
		   (LGLL)s->prune.child.confs,
		   (LGLL)s->prune.child.decs,
		   (LGLL)s->prune.child.props);

	lglprs(lgl,
		   "quat: %d count, %lld quat, %lld self1, %lld self2, %lld dup",
		   s->quatres.count,
		   (LGLL)s->quatres.quat,
		   (LGLL)s->quatres.self1,
		   (LGLL)s->quatres.self2,
		   (LGLL)s->quatres.dup);

	lglprs(lgl,
		   "queu: %lld flushed, %lld sorted, %lld decisions %.3f%%",
		   (LGLL)s->queue.flushed,
		   (LGLL)s->queue.sorted,
		   (LGLL)s->qdecs,
		   lglpcnt(s->qdecs, s->decisions));

	lglprs(lgl,
		   "redc: %lld clauses %.0f%%, %lld tried %.0f%%, %lld sat %.0f%%",
		   (LGLL)s->redcls.cls.red,
		   lglpcnt(s->redcls.cls.red, s->redcls.cls.tried),
		   (LGLL)s->redcls.cls.tried,
		   lglpcnt(s->redcls.cls.tried, s->confs),
		   (LGLL)s->redcls.cls.sat,
		   lglpcnt(s->redcls.cls.sat, s->redcls.cls.tried));
	lglprs(lgl,
		   "redc: %lld lits %.0f%%, %lld tried %.0f%%, %lld sat %.0f%%",
		   (LGLL)s->redcls.lits.red,
		   lglpcnt(s->redcls.lits.red, s->redcls.lits.tried),
		   (LGLL)s->redcls.lits.tried,
		   lglpcnt(s->redcls.lits.tried, s->lits.nonmin),
		   (LGLL)s->redcls.lits.sat,
		   lglpcnt(s->redcls.lits.sat, s->redcls.lits.tried));
	lglprs(lgl,
		   "redc: %lld times jump level reduced, %.1f levels on average",
		   (LGLL)s->redcls.jlevel.red,
		   lglavg(s->redcls.jlevel.sum, s->redcls.jlevel.red));

	lglprs(lgl,
		   "reds: %d count, %.1f conflicts per reduce, %d reset",
		   s->reduced.count,
		   lglavg(s->confs, s->reduced.count),
		   s->reduced.reset);
	lglprs(lgl,
		   "reds: %d memlim %.0f%%, %d arith %.0f%%",
		   s->reduced.memlim,
		   lglpcnt(s->reduced.memlim, s->reduced.count),
		   s->reduced.arith,
		   lglpcnt(s->reduced.arith, s->reduced.count));
	sum = s->reduced.collected + s->reduced.retired;
	lglprs(lgl,
		   "reds: %lld collected %.0f%%, %lld retired %.0f%%",
		   s->reduced.collected,
		   lglpcnt(s->reduced.collected, sum),
		   s->reduced.retired,
		   lglpcnt(s->reduced.retired, sum));

	lglprs(lgl,
		   "rmbd: %d removed, %d red %.0f%%",
		   s->bindup.removed,
		   s->bindup.red,
		   lglpcnt(s->bindup.red, s->bindup.removed));

	lglprs(lgl,
		   "rsts: %lld restarts %.0f%% of checked, %lld skipped %.0f%%",
		   (LGLL)s->restarts.count,
		   lglpcnt(s->restarts.count, s->restarts.checked),
		   (LGLL)s->restarts.skipped,
		   lglpcnt(s->restarts.skipped, s->restarts.checked));
	lglprs(lgl,
		   "rsts: %lld checks %.0f%%, %lld forced %.0f%%",
		   (LGLL)s->restarts.checked,
		   lglpcnt(s->restarts.checked, s->confs),
		   (LGLL)s->restarts.forced,
		   lglpcnt(s->restarts.forced, s->restarts.checked));
	lglprs(lgl,
		   "rsts: %lld not forced (%.0f%% of checked, %.0f%% of skipped)",
		   (LGLL)s->restarts.notforced,
		   lglpcnt(s->restarts.notforced, s->restarts.checked),
		   lglpcnt(s->restarts.notforced, s->restarts.skipped));
	lglprs(lgl,
		   "rsts: %lld agile %.0f%% of skipped, %lld delayed %.0f%%",
		   (LGLL)s->restarts.agile,
		   lglpcnt(s->restarts.agile, s->restarts.skipped),
		   (LGLL)s->restarts.delayed,
		   lglpcnt(s->restarts.delayed, s->restarts.skipped));
	lglprs(lgl,
		   "rsts: %lld blocked %.2f%% restart, %lld reused %.0f%%",
		   (LGLL)s->restarts.blocked,
		   lglpcnt(s->restarts.blocked, s->restarts.count),
		   (LGLL)s->restarts.reused,
		   lglpcnt(s->restarts.reused, s->restarts.skipped));
	lglprs(lgl,
		   "rsts: %lld partial reuse %.1f%% of restarts, kept %.1f%%",
		   (LGLL)s->restarts.kept.count,
		   lglpcnt(s->restarts.kept.count, s->restarts.count),
		   lglavg(s->restarts.kept.sum, s->restarts.kept.count));
	lglprs(lgl,
		   "rsts: %lld delta %.1f avg, %lld penalty %.1f avg",
		   (LGLL)s->restarts.delta.delta,
		   lglavg(s->restarts.delta.delta, s->restarts.delta.count),
		   (LGLL)s->restarts.delta.pen,
		   lglavg(s->restarts.delta.pen, s->restarts.delta.count));

	lglprs(lgl,
		   "scic: %lld set, %.1f%% min, %.1f%% max",
		   (LGLL)s->setscincf.count,
		   s->setscincf.min / 10.0,
		   s->setscincf.max / 10.0);

	lglprs(lgl,
		   "simp: %d count, %d confs %0.f%%, %d vars %.0f%%",
		   s->simp.count,
		   s->simp.limhit.confs,
		   lglpcnt(s->simp.limhit.confs, s->simp.count),
		   s->simp.limhit.vars,
		   lglpcnt(s->simp.limhit.vars, s->simp.count));
	lglprs(lgl,
		   "simp: %d its %0.f%%, %d bin %.0f%%, %d trns %.0f%%",
		   s->simp.limhit.its,
		   lglpcnt(s->simp.limhit.its, s->simp.count),
		   s->simp.limhit.bin,
		   lglpcnt(s->simp.limhit.bin, s->simp.count),
		   s->simp.limhit.trn,
		   lglpcnt(s->simp.limhit.trn, s->simp.count));

	lglprs(lgl,
		   "subl: %lld count (%lld tried %.1f), %lld sub out of %lld (%.1f%%)",
		   s->subl.count,
		   s->subl.tried,
		   lglavg(s->subl.tried, s->subl.count),
		   s->subl.sub,
		   s->subl.cands,
		   lglpcnt(s->subl.sub, s->subl.cands));

	lglprs(lgl,
		   "swps: %d count, %lld rounds, %lld queries (%.1f/round)",
		   s->sweep.count,
		   (LGLL)s->sweep.rounds,
		   (LGLL)s->sweep.queries.total,
		   lglavg(s->sweep.queries.total, s->sweep.rounds));
	lglprs(lgl,
		   "swps: %lld sat calls, %lld unsat (%.1f%% success rate)",
		   s->sweep.sat,
		   s->sweep.unsat,
		   lglavg(s->sweep.confs, s->sweep.sat));
	lglprs(lgl,
		   "swps: %lld decisions %.1f, %lld conflicts %.1f",
		   (LGLL)s->sweep.decs,
		   lglavg(s->sweep.decs, s->sweep.sat),
		   (LGLL)s->sweep.confs,
		   lglavg(s->sweep.confs, s->sweep.sat));
	lglprs(lgl,
		   "swps; %.1f avg class size, %.1f avg environment size",
		   lglavg(s->sweep.sumsize.classes, s->sweep.queries.total),
		   lglavg(s->sweep.sumsize.envs, s->sweep.sat));
	lglprs(lgl, "swps: %d failed, %d impls, %d equivs", s->sweep.failed, s->sweep.impls, s->sweep.equivs);

	lglprs(lgl,
		   "swp0: %lld type 0 queries, %lld unsat (%.1f%%), %lld sat (%.1f%%)",
		   (LGLL)s->sweep.queries.type[0].count,
		   (LGLL)s->sweep.queries.type[0].unsat,
		   lglpcnt(s->sweep.queries.type[0].unsat, s->sweep.queries.type[0].count),
		   (LGLL)s->sweep.queries.type[0].sat,
		   lglpcnt(s->sweep.queries.type[0].sat, s->sweep.queries.type[0].count));
	lglprs(lgl,
		   "swp1: %lld type 1 queries, %lld unsat (%.1f%%), %lld sat (%.1f%%)",
		   (LGLL)s->sweep.queries.type[1].count,
		   (LGLL)s->sweep.queries.type[1].unsat,
		   lglpcnt(s->sweep.queries.type[1].unsat, s->sweep.queries.type[1].count),
		   (LGLL)s->sweep.queries.type[1].sat,
		   lglpcnt(s->sweep.queries.type[1].sat, s->sweep.queries.type[1].count));
	lglprs(lgl,
		   "swp2: %lld type 2 queries, %lld unsat (%.1f%%), %lld sat (%.1f%%)",
		   (LGLL)s->sweep.queries.type[2].count,
		   (LGLL)s->sweep.queries.type[2].unsat,
		   lglpcnt(s->sweep.queries.type[2].unsat, s->sweep.queries.type[2].count),
		   (LGLL)s->sweep.queries.type[2].sat,
		   lglpcnt(s->sweep.queries.type[2].sat, s->sweep.queries.type[2].count));

	lglprs(lgl,
		   "sync: %lld produced units, %lld successful consumptions %.0f%%",
		   s->sync.units.produced,
		   s->sync.units.consumed.actual,
		   lglpcnt(s->sync.units.consumed.actual, s->sync.units.produced));
	lglprs(lgl,
		   "sync: %lld consume units calls, %lld tried %.0f%%, %lld success %.0f%%",
		   s->sync.units.consumed.calls,
		   s->sync.units.consumed.tried,
		   lglpcnt(s->sync.units.consumed.tried, s->sync.units.consumed.calls),
		   s->sync.units.consumed.actual,
		   lglpcnt(s->sync.units.consumed.actual, s->sync.units.consumed.calls));
	lglprs(lgl,
		   "sync: %lld produced clauses, %lld successful consumptions %.0f%%",
		   s->sync.cls.produced,
		   s->sync.cls.consumed.actual,
		   lglpcnt(s->sync.cls.consumed.actual, s->sync.cls.produced));
	lglprs(lgl,
		   "sync: %lld consume clause calls, %lld tried %.0f%%, %lld success %.0f%%",
		   s->sync.cls.consumed.calls,
		   s->sync.cls.consumed.tried,
		   lglpcnt(s->sync.cls.consumed.tried, s->sync.cls.consumed.calls),
		   s->sync.cls.consumed.actual,
		   lglpcnt(s->sync.cls.consumed.actual, s->sync.cls.consumed.calls));

	lglprs(lgl, "time: %lld calls to obtain time", (LGLL)s->times);

	lglprs(lgl,
		   "tops: %d fixed %.0f%%, %d its, %.2f confs/it",
		   s->fixed.sum,
		   lglpcnt(s->fixed.sum, lgl->maxext),
		   s->its.count,
		   lglavg(s->confs, s->its.count));

	lglprs(lgl, "trds: %d transitive reductions, %d removed, %d failed", s->trd.count, s->trd.red, s->trd.failed);
	lglprs(lgl, "trds: %lld nodes, %lld edges, %lld steps", (LGLL)s->trd.lits, (LGLL)s->trd.bins, (LGLL)s->trd.steps);

	lglprs(lgl, "trim: %lld words, %.1f MB", (LGLL)s->trims, (s->trims * sizeof(int)) / (double)(1 << 20));

	lglprs(lgl,
		   "trnr: %d count, %d bin, %d trn, %lld steps",
		   s->ternres.count,
		   s->ternres.bin,
		   s->ternres.trn,
		   (LGLL)s->ternres.steps);

	lglprs(lgl, "unhd: %d count, %d rounds, %lld steps", s->unhd.count, s->unhd.rounds, (LGLL)s->unhd.steps);
	lglprs(lgl,
		   "unhd: %d non-trivial sccs of average size %.1f",
		   s->unhd.stamp.sccs,
		   lglavg(s->unhd.stamp.sumsccsizes, s->unhd.stamp.sccs));
	sum = lglunhdunits(lgl);
	lglprs(
		lgl, "unhd: %lld units, %d bin, %d trn, %d lrg", sum, s->unhd.units.bin, s->unhd.units.trn, s->unhd.units.lrg);
	sum = lglunhdfailed(lgl);
	lglprs(lgl,
		   "unhd: %lld failed, %d stamp, %d lits, %d bin, %d trn, %d lrg",
		   sum,
		   s->unhd.stamp.failed,
		   s->unhd.failed.lits,
		   s->unhd.failed.bin,
		   s->unhd.failed.trn,
		   s->unhd.units.lrg);
	sum = lglunhdtauts(lgl);
	lglprs(lgl,
		   "unhd: %lld tauts, %d bin %.0f%%, %d trn %.0f%%, %d lrg %.0f%%",
		   sum,
		   s->unhd.tauts.bin,
		   lglpcnt(s->unhd.tauts.bin, sum),
		   s->unhd.tauts.trn,
		   lglpcnt(s->unhd.tauts.trn, sum),
		   s->unhd.tauts.lrg,
		   lglpcnt(s->unhd.tauts.lrg, sum));
	lglprs(lgl,
		   "unhd: %lld tauts, %d stamp %.0f%%, %d red %.0f%%",
		   sum,
		   s->unhd.stamp.trds,
		   lglpcnt(s->unhd.stamp.trds, sum),
		   s->unhd.tauts.red,
		   lglpcnt(s->unhd.tauts.red, sum));
	sum = lglunhdhbrs(lgl);
	lglprs(lgl,
		   "unhd: %lld hbrs, %d trn %.0f%%, %d lrg %.0f%%, %d red %.0f%%",
		   sum,
		   s->unhd.hbrs.trn,
		   lglpcnt(s->unhd.hbrs.trn, sum),
		   s->unhd.hbrs.lrg,
		   lglpcnt(s->unhd.hbrs.lrg, sum),
		   s->unhd.hbrs.red,
		   lglpcnt(s->unhd.hbrs.red, sum));
	sum = lglunhdstrd(lgl);
	lglprs(lgl,
		   "unhd: %lld str, %d bin %.0f%%, %d trn %.0f%%, %d lrg %.0f%%, %d red %.0f%%",
		   sum,
		   s->unhd.units.bin,
		   lglpcnt(s->unhd.units.bin, sum),
		   s->unhd.str.trn,
		   lglpcnt(s->unhd.str.trn, sum),
		   s->unhd.str.lrg,
		   lglpcnt(s->unhd.str.lrg, sum),
		   s->unhd.str.red,
		   lglpcnt(s->unhd.str.red, sum));

	removed = s->fixed.sum + s->elm.elmd + s->equiv.sum;
	remaining = lgl->maxext - removed;
	assert(remaining >= 0);
	lglprs(lgl,
		   "vars: %d remaining %.0f%% and %d removed %.0f%% out of %d",
		   remaining,
		   lglpcnt(remaining, lgl->maxext),
		   removed,
		   lglpcnt(removed, lgl->maxext),
		   lgl->maxext);
	lglprs(lgl,
		   "vars: %d fixed %.0f%%, %d eliminated %.0f%%, %d equivalent %.0f%%",
		   s->fixed.sum,
		   lglpcnt(s->fixed.sum, lgl->maxext),
		   s->elm.elmd,
		   lglpcnt(s->elm.elmd, lgl->maxext),
		   s->equiv.sum,
		   lglpcnt(s->equiv.sum, lgl->maxext));

	visits = s->visits.search + s->visits.simp + s->visits.lkhd;
	lglprs(lgl,
		   "vsts: %lld visits, %.0f%% srch, %.0f%% simp, %.0f%% lkhd",
		   (LGLL)visits,
		   lglpcnt(s->visits.search, visits),
		   lglpcnt(s->visits.simp, visits),
		   lglpcnt(s->visits.lkhd, visits));
	lglprs(lgl,
		   "vsts: %.1f search visits per propagation, %.1f per conflict",
		   lglavg(s->visits.search, s->props.search),
		   lglavg(s->visits.search, s->confs));

	lglprs(lgl, "wchs: %lld pushed, %lld enlarged, %d defrags", (LGLL)s->pshwchs, (LGLL)s->enlwchs, s->defrags);

	lglprsline(lgl);
	lglgluestats(lgl);
	lglprsline(lgl);

#ifndef NLGLDRUPLIG
	if (lgl->druplig) {
		druplig_stats(lgl->druplig, lgl->out);
		lglprsline(lgl);
	}
#endif

SHORT:
	lglprof(lgl);
	lglprsline(lgl);
	lglprs(lgl, "%13lld conflicts,    %10.1f confs/sec", (LGLL)s->confs, lglavg(s->confs, t));
	lglprs(lgl, "%13lld ternaries,    %10.1f confs/ternary", (LGLL)s->trns, lglavg(s->confs, s->trns));
	lglprs(lgl, "%13lld binaries,     %10.1f confs/binary", (LGLL)s->bins, lglavg(s->confs, s->bins));
	lglprs(lgl, "%13lld iterations,   %10.1f confs/iteration", (LGLL)s->its.count, lglavg(s->confs, s->its.count));
	lglprsline(lgl);
	lglprs(lgl,
		   "%13lld reductions,   %10.1f redus/sec, %8.1f confs/reduction",
		   (LGLL)s->reduced.count,
		   lglavg(s->reduced.count, t),
		   lglavg(s->confs, s->reduced.count));
	lglprs(lgl,
		   "%13lld restarts,     %10.1f rests/sec, %8.1f confs/restart",
		   (LGLL)s->restarts.count,
		   lglavg(s->restarts.count, t),
		   lglavg(s->confs, s->restarts.count));
	lglprs(lgl,
		   "%13lld decisions,    %10.1f decis/sec, %8.1f decis/conflict",
		   (LGLL)s->decisions,
		   lglavg(s->decisions, t),
		   lglavg(s->decisions, s->confs));
	lglprs(lgl,
		   "%13lld propagations, %10.1f props/sec, %8.1f props/decision",
		   (LGLL)p,
		   lglavg(p, t),
		   lglavg(p, s->decisions));
	lglprsline(lgl);
	lglprs(lgl, "%.1f seconds, %.1f MB", t, lglmaxmb(lgl));
	fflush(lgl->out);
}

int64_t
lglgetprops(LGL* lgl)
{
	REQINITNOTFORKED();
	return lgl->stats->props.search + lgl->stats->props.simp;
}

int64_t
lglgetrestarts(LGL* lgl)
{
	REQINITNOTFORKED();
	return lgl->stats->restarts.count;
}

int64_t
lglgetconfs(LGL* lgl)
{
	REQINITNOTFORKED();
	return lgl->stats->confs;
}

int64_t
lglgetdecs(LGL* lgl)
{
	REQINITNOTFORKED();
	return lgl->stats->decisions;
}

void
lglsizes(LGL* lgl)
{
	lglprt(lgl, 0, "sizeof (int) == %ld", (long)sizeof(int));
	lglprt(lgl, 0, "sizeof (unsigned) == %ld", (long)sizeof(unsigned));
	lglprt(lgl, 0, "sizeof (void*) == %ld", (long)sizeof(void*));
	lglprt(lgl, 0, "sizeof (Stk) == %ld", (long)sizeof(Stk));
	lglprt(lgl, 0, "sizeof (Fun) == %ld", (long)sizeof(Fun));
	lglprt(lgl, 0, "sizeof (AVar) == %ld", (long)sizeof(AVar));
	lglprt(lgl, 0, "sizeof (DVar) == %ld", (long)sizeof(DVar));
	lglprt(lgl, 0, "sizeof (QVar) == %ld", (long)sizeof(QVar));
	lglprt(lgl, 0, "sizeof (EVar) == %ld", (long)sizeof(EVar));
	lglprt(lgl, 0, "sizeof (Stats.lir) == %ld", (long)sizeof((Stats*)0)->lir);
	lglprt(lgl, 0, "sizeof (Stats) == %ld", (long)sizeof(Stats));
	lglprt(lgl, 0, "sizeof (LGL) == %ld", (long)sizeof(LGL));
	lglprt(lgl, 0, "MAXVAR == %ld", (long)MAXVAR);
	lglprt(lgl, 0, "MAXREDLIDX == %ld", (long)MAXREDLIDX);
	lglprt(lgl, 0, "MAXIRRLIDX == %ld", (long)MAXIRRLIDX);
}

#define LGLRELSTK(MGR, STKPTR)                                                                                         \
	do {                                                                                                               \
		assert(lglmtstk(STKPTR));                                                                                      \
		lglrelstk((MGR), (STKPTR));                                                                                    \
	} while (0)

void
lglrelease(LGL* lgl)
{
	lgldealloc dealloc;
	int i;

	REQINIT();
	if (lgl->clone)
		lglrelease(lgl->clone), lgl->clone = 0;
	TRAPI("release");

#ifndef NLGLDRUPLIG
	if (lgl->druplig)
		druplig_reset(lgl->druplig), lgl->druplig = 0;
#endif

	// Heap state starts here:

	DEL(lgl->avars, lgl->szvars);
	DEL(lgl->doms, 2 * lgl->szvars);
	DEL(lgl->drail, lgl->szdrail);
	DEL(lgl->dvars, lgl->szvars);
	DEL(lgl->ext, lgl->szext);
	DEL(lgl->i2e, lgl->szvars);
	DEL(lgl->jwh, 2 * lgl->szvars);
	DEL(lgl->qvars, lgl->szvars);
	DEL(lgl->vals, lgl->szvars);

	lglrelctk(lgl, &lgl->control);

	lglrelstk(lgl, &lgl->assume);
#ifndef NDEBUG
	lglrelstk(lgl, &lgl->prevclause);
#endif
	lglrelstk(lgl, &lgl->clause);
	lglrelstk(lgl, &lgl->dsched);
	lglrelstk(lgl, &lgl->queue.stk);
	lglrelstk(lgl, &lgl->eassume);
	lglrelstk(lgl, &lgl->eclause);
	lglrelstk(lgl, &lgl->extend);
	lglrelstk(lgl, &lgl->learned);
	lglrelstk(lgl, &lgl->frames);
	lglrelstk(lgl, &lgl->promote);
	lglrelstk(lgl, &lgl->trail);
	lglrelstk(lgl, &lgl->wchs->stk);

#ifndef NCHKSOL
	lglrelstk(lgl, &lgl->orig);
#endif

	lglrelstk(lgl, &lgl->irr);
	for (i = 0; i <= MAXGLUE; i++)
		lglrelstk(lgl, &lgl->red[i]);

	// The following heap allocated memory has no state:

	LGLRELSTK(lgl, &lgl->lcaseen);
	LGLRELSTK(lgl, &lgl->resolvent);
	LGLRELSTK(lgl, &lgl->minstk);
	LGLRELSTK(lgl, &lgl->poisoned);
	LGLRELSTK(lgl, &lgl->seen);

	lglrelstk(lgl, &lgl->saved.bin);
	lglrelstk(lgl, &lgl->saved.trn);

	DEL(lgl->limits, 1);
	DEL(lgl->times, 1);
	DEL(lgl->timers, 1);
	DEL(lgl->red, MAXGLUE + 1);
	DEL(lgl->wchs, 1);

	if (lgl->fltstr)
		DEL(lgl->fltstr, 1);
	if (lgl->cbs)
		DEL(lgl->cbs, 1);		 // next to last
	lgldelstr(lgl, lgl->prefix); // last

	// adjust upfront the mem counters ...

	lgldec(lgl, sizeof *lgl->stats);
	lgldec(lgl, sizeof *lgl->opts);
	lgldec(lgl, sizeof *lgl->mem);
	lgldec(lgl, sizeof *lgl);

	assert(getenv("LGLEAK") || !lgl->stats->bytes.current);

	if (lgl->closeapitrace == 1)
		fclose(lgl->apitrace);
	if (lgl->closeapitrace == 2)
		pclose(lgl->apitrace);

	if ((dealloc = lgl->mem->dealloc)) {
		void* memstate = lgl->mem->state;
		if (lgl->stats)
			dealloc(memstate, lgl->stats, sizeof *lgl->stats);
		if (lgl->times)
			dealloc(memstate, lgl->times, sizeof *lgl->times);
		if (lgl->opts)
			dealloc(memstate, lgl->opts, sizeof *lgl->opts);
		assert(lgl->mem);
		dealloc(memstate, lgl->mem, sizeof *lgl->mem);
		dealloc(memstate, lgl, sizeof *lgl);
	} else {
		free(lgl->stats);
		free(lgl->times);
		free(lgl->opts);
		free(lgl->mem);
		free(lgl);
	}
}

void
lglutrav(LGL* lgl, void* state, void (*trav)(void*, int))
{
	int elit, val;
	REQINITNOTFORKED();
	if (!lgl->mt && !lglbcp(lgl))
		lglmt(lgl);
	if (!lgl->mt)
		lglgc(lgl);
	if (lgl->mt)
		return;
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	for (elit = 1; elit <= lgl->maxext; elit++) {
		if (!(val = lglefixed(lgl, elit)))
			continue;
		trav(state, (val < 0) ? -elit : elit);
	}
}

void
lgletrav(LGL* lgl, void* state, void (*trav)(void*, int, int))
{
	int elit, erepr;
	REQINITNOTFORKED();
	if (!lgl->mt && !lglbcp(lgl))
		lglmt(lgl);
	if (!lgl->mt)
		lglgc(lgl);
	if (lgl->mt)
		return;
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	for (elit = 1; elit <= lgl->maxext; elit++) {
		if (lglefixed(lgl, elit))
			continue;
		erepr = lglerepr(lgl, elit);
		if (erepr == elit)
			continue;
		trav(state, elit, erepr);
	}
}

static void
lglictrav(LGL* lgl, int internal, int redtoo, void* state, void (*trav)(void*, int red, int lit))
{
	int idx, sign, lit, blit, tag, red, other, other2;
	const int *p, *w, *eow, *c;
	int glue;
	HTS* hts;
	REQINITNOTFORKED();
	if (lgl->mt) {
		trav(state, 0, 0);
		return;
	}
	for (idx = 2; idx < lgl->nvars; idx++) {
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				red = blit & REDCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (!redtoo && red)
					continue;
				if (tag != BINCS && tag != TRNCS)
					continue;
				other = blit >> RMSHFT;
				if (abs(other) < idx)
					continue;
				if (tag == TRNCS) {
					other2 = *p;
					if (abs(other2) < idx)
						continue;
				} else
					other2 = 0;
				trav(state, red, internal ? lit : lglexport(lgl, lit));
				trav(state, red, internal ? other : lglexport(lgl, other));
				if (other2)
					trav(state, red, internal ? other2 : lglexport(lgl, other2));
				trav(state, red, 0);
			}
		}
	}
	for (glue = -1; glue < MAXGLUE; glue++) {
		const int* start = glue < 0 ? lgl->irr.start : lgl->red[glue].start;
		const int* top = glue < 0 ? lgl->irr.top : lgl->red[glue].top;
		const int red = (glue >= 0);
		if (!redtoo && red)
			break;
		for (c = start; c < top; c = p + 1) {
			p = c;
			if (*p >= NOTALIT)
				continue;
			while ((other = *p)) {
				trav(state, red, internal ? other : lglexport(lgl, other));
				p++;
			}
			trav(state, red, 0);
		}
	}
}

struct IrrTrav
{
	void* state;
	void (*trav)(void*, int);
};

typedef struct IrrTrav IrrTrav;

static void
lglirrtrav(void* state, int red, int lit)
{
	IrrTrav* irrtrav = state;
	assert(!red), (void)red;
	irrtrav->trav(irrtrav->state, lit);
}

void
lglctrav(LGL* lgl, void* state, void (*trav)(void*, int))
{
	IrrTrav irrtrav;
	irrtrav.state = state;
	irrtrav.trav = trav;
	if (lgl->level)
		lglbacktrack(lgl, 0);
	if (!lgl->mt && !lglbcp(lgl))
		lglmt(lgl);
	if (!lgl->mt)
		lglgc(lgl);
	lglictrav(lgl, 0, 0, &irrtrav, lglirrtrav);
}

static void
lgltravcounter(void* voidptr, int lit)
{
	int* cntptr = voidptr;
	if (!lit)
		*cntptr += 1;
}

static void
lgltravprinter(void* voidptr, int lit)
{
	FILE* out = voidptr;
	if (lit)
		fprintf(out, "%d ", lit);
	else
		fprintf(out, "0\n");
}

void
lglprint(LGL* lgl, FILE* file)
{
	int count = 0;
	lglctrav(lgl, &count, lgltravcounter);
	fprintf(file, "p cnf %d %d\n", lglmaxvar(lgl), count);
	lglctrav(lgl, file, lgltravprinter);
}

void
lglprintall(LGL* lgl, FILE* file)
{
	int count = 0;
	lgltravall(lgl, &count, lgltravcounter);
	fprintf(file, "p cnf %d %d\n", lglmaxvar(lgl), count);
	lgltravall(lgl, file, lgltravprinter);
}

void
lglrtrav(LGL* lgl, void* state, void (*trav)(void*, int, int))
{
	int idx, sign, lit, blit, tag, red, other, other2, glue;
	const int *p, *c, *w, *eow;
	Stk* lir;
	HTS* hts;
	REQINITNOTFORKED();
	if (lgl->mt)
		return;
	lglgc(lgl);
	if (lgl->level > 0)
		lglbacktrack(lgl, 0);
	for (idx = 2; idx < lgl->nvars; idx++) {
		if (lglval(lgl, idx))
			continue;
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				red = blit & REDCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (!red)
					continue;
				if (tag != BINCS && tag != TRNCS)
					continue;
				other = blit >> RMSHFT;
				if (abs(other) < idx)
					continue;
				assert(!lglval(lgl, other));
				if (tag == TRNCS) {
					other2 = *p;
					if (abs(other2) < idx)
						continue;
					assert(!lglval(lgl, other2));
				} else
					other2 = 0;
				trav(state, lglexport(lgl, lit), 0);
				trav(state, lglexport(lgl, other), 0);
				if (other2)
					trav(state, lglexport(lgl, other2), 0);
				trav(state, 0, 0);
			}
		}
	}
	for (glue = 0; glue < MAXGLUE; glue++) {
		lir = lgl->red + glue;
		for (c = lir->start; c < lir->top; c = p + 1) {
			p = c;
			if (*p >= NOTALIT)
				continue;
			while ((other = *p)) {
				assert(!lglval(lgl, other));
				trav(state, lglexport(lgl, other), 0);
				p++;
			}
			trav(state, 0, 0);
		}
	}
}

static void
lgltravallu(void* voidptr, int unit)
{
	Trv* state = voidptr;
	state->trav(state->state, unit);
	state->trav(state->state, 0);
}

static void
lgltravalle(void* voidptr, int lit, int repr)
{
	Trv* state = voidptr;
	state->trav(state->state, -lit);
	state->trav(state->state, repr);
	state->trav(state->state, 0);
	state->trav(state->state, lit);
	state->trav(state->state, -repr);
	state->trav(state->state, 0);
}

void
lgltravall(LGL* lgl, void* state, void (*trav)(void*, int))
{
	Trv travstate;
	travstate.state = state;
	travstate.trav = trav;
	lglutrav(lgl, &travstate, lgltravallu);
	lgletrav(lgl, &travstate, lgltravalle);
	lglctrav(lgl, state, trav);
}

#ifndef NDEBUG

void
lgldump(LGL* lgl)
{
	int idx, sign, lit, blit, tag, red, other, other2, glue;
	const int *p, *w, *eow, *c, *top;
	Stk* lir;
	HTS* hts;
	for (idx = 2; idx < lgl->nvars; idx++)
		for (sign = -1; sign <= 1; sign += 2) {
			lit = sign * idx;
			hts = lglhts(lgl, lit);
			w = lglhts2wchs(lgl, hts);
			eow = w + hts->count;
			for (p = w; p < eow; p++) {
				blit = *p;
				tag = blit & MASKCS;
				red = blit & REDCS;
				if (tag == TRNCS || tag == LRGCS)
					p++;
				if (tag == BINCS) {
					other = blit >> RMSHFT;
					if (abs(other) < idx)
						continue;
					other2 = 0;
				} else if (tag == TRNCS) {
					other = blit >> RMSHFT;
					if (abs(other) < idx)
						continue;
					other2 = *p;
					if (abs(other2) < idx)
						continue;
				} else
					continue;
				fprintf(lgl->out, "%s %d %d", red ? "red" : "irr", lit, other);
				if (tag == TRNCS)
					fprintf(lgl->out, " %d", other2);
				fprintf(lgl->out, "\n");
			}
		}
	top = lgl->irr.top;
	for (c = lgl->irr.start; c < top; c = p + 1) {
		p = c;
		if (*p >= NOTALIT)
			continue;
		fprintf(lgl->out, "irr");
		while (*p)
			fprintf(lgl->out, " %d", *p++);
		fprintf(lgl->out, "\n");
	}
	for (glue = 0; glue < MAXGLUE; glue++) {
		lir = lgl->red + glue;
		top = lir->top;
		for (c = lir->start; c < top; c = p + 1) {
			p = c;
			if (*p >= NOTALIT)
				continue;
			fprintf(lgl->out, "glue%d", glue);
			while (*p)
				fprintf(lgl->out, " %d", *p++);
			fprintf(lgl->out, "\n");
		}
	}
}

#endif

static void
lglforkadd(void* ptr, int red, int lit)
{
	assert(!red);
	lgladd(ptr, lit ? lglforklit(lit) : 0);
	(void)red;
}

LGL*
lglfork(LGL* parent)
{
	LGL* child;
	{
		LGL* lgl = parent;
		REQINIT();
		ABORTIF(!lglmtstk(&parent->eassume), "can not fork under assumptions");
		ABORTIF(parent->forked == INT_MAX, "parent forked too often");
	}
	if (parent->level > 0)
		lglbacktrack(parent, 0);
	(void)lglbcp(parent);
	lglgc(parent);
	child = lglminit(parent->mem->state, parent->mem->alloc, parent->mem->realloc, parent->mem->dealloc);
	child->parent = parent;
	lglcopyclonenfork(child, parent);
	if (parent->level)
		lglbacktrack(parent, 0);
	if (!parent->mt && !lglbcp(parent))
		lglmt(parent);
	if (!parent->mt)
		lglgc(parent);
	lglictrav(parent, 1, 0, child, lglforkadd);
	assert(parent->stats->irr.clauses.cur == child->stats->irr.clauses.cur);
	parent->forked++;
	assert(parent->forked > 0);
	lglprt(parent, 1, "forked-%d", parent->forked);
	return child;
}

static void
lglflass(LGL* lgl, LGL* from)
{
	int idx, copied = 0;
	assert(!lgl->level), assert(!lgl->mt);
	assert((from->state & (SATISFIED | EXTENDED)));
	if (lgl->nvars)
		assert(lgl->nvars == from->maxext + 2);
	else
		assert(!from->maxext);
	lglreset(lgl);
	for (idx = 2; idx < lgl->nvars; idx++) {
		int val = lglderef(from, idx - 1);
		int lit = val < 0 ? -idx : idx;
		lgldassume(lgl, lit);
		copied++;
	}
	lglprt(lgl, 1, "[flass] copied %d internal assignments", copied);
	TRANS(SATISFIED);
	lglextend(lgl);
#ifndef NCHKSOL
	lglchksol(lgl);
#endif
}

int
lgljoin(LGL* parent, LGL* child)
{
	int res;
	{
		LGL* lgl = parent;
		ABORTIF(!parent, "uninitialized parent manager");
		ABORTIF(!child, "uninitialized child manager");
		ABORTIF(!parent->forked, "parent manager not forked");
		ABORTIF(!child->parent, "child manager has not parent");
		ABORTIF(child->parent != parent, "child manager has different parent");
		ABORTIF(!lglmtstk(&child->eassume), "child manager with assumptions not supported yet");
	}
	if (child->mt || (child->state & UNSATISFIED)) {
#ifndef NLGLOG
		LGL* lgl = parent;
#endif
		assert(child->mt); // no assumptions yet ...
		lglprt(parent, 1, "[join] unsatisfied state");
		if (!parent->mt) {
			LOG(1, "joining empty clause");
			parent->mt = 1;
		} else
			LOG(1, "no need to join empty clause since parent has one");
		res = 20;
	} else if (child->state & (SATISFIED | EXTENDED)) {
		lglprt(parent, 1, "[join] satisfied state");
		lglflass(parent, child);
		res = 10;
	} else {
		lglprt(parent, 1, "[join] unknown state");
		lglreset(parent);
		{
			LGL* lgl = parent;
			TRANS(UNKNOWN);
		}
		res = 0;
	}
	return res;
}

/*------------------------------------------------------------------------*/

static int
lglnextch(FILE* in, int* linenoptr)
{
	int res = getc(in);
	if (res == '\n')
		*linenoptr += 1;
	return res;
}

static char isoptchartab[256];
static int isoptchartabinitialized;

static int
isoptchar(unsigned char uch)
{
	int i;
	if (!isoptchartabinitialized) {
		for (i = 'a'; i <= 'z'; i++)
			isoptchartab[i] = 1;
		for (i = 'A'; i <= 'Z'; i++)
			isoptchartab[i] = 1;
		for (i = '0'; i <= '9'; i++)
			isoptchartab[i] = 1;
		isoptchartab['-'] = isoptchartab['_'] = 1;
		isoptchartabinitialized = 1;
	}
	return isoptchartab[uch];
}

static void
lglenlbuf(LGL* lgl, Buf* b)
{
	size_t new_size = b->size ? 2 * b->size : 2;
	RSZ(b->start, b->size, new_size);
	b->size = new_size;
}

static void
lglpushbuf(LGL* lgl, Buf* buf, int ch)
{
	if (buf->count + 1 >= buf->size)
		lglenlbuf(lgl, buf);
	assert(buf->count + 1 < buf->size);
	buf->start[buf->count++] = ch;
	buf->start[buf->count] = 0;
}

const char*
lglparsefile(LGL* lgl, FILE* in, int force, int* lp, int* mp)
{
	int ch, prev, m, n, v, c, l, lit, sign, val, embedded = 0, header, section;
	Buf buf;
	CLR(buf);
SKIP:
	ch = lglnextch(in, lp);
	if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
		goto SKIP;
	if (ch == 'c') {
		ch = lglnextch(in, lp);
		while (ch != '\n') {
			if (ch == EOF)
				return "end of file in comment";
			prev = ch;
			ch = lglnextch(in, lp);
			if (prev != '-')
				continue;
			if (ch != '-')
				continue;
			assert(!buf.count);
			ch = lglnextch(in, lp);
			while (isoptchar(ch)) {
				assert(ch != '\n');
				lglpushbuf(lgl, &buf, ch);
				ch = lglnextch(in, lp);
			}
			buf.count = 0;
			if (ch != '=')
				continue;
			ch = lglnextch(in, lp);
			if (ch == '-')
				sign = -1, ch = lglnextch(in, lp);
			else
				sign = 1;
			if (!isdigit(ch))
				continue;
			val = ch - '0';
			while (isdigit(ch = lglnextch(in, lp)))
				val = 10 * val + (ch - '0');

			if (!lglhasopt(lgl, buf.start)) {
				lglwrn(lgl, "parsed invalid embedded option '--%s'\n", buf.start);
				continue;
			}

			val *= sign;

			if (!embedded++)
				lglprt(lgl, 1, ""), lglprt(lgl, 1, "embedded options"), lglprt(lgl, 1, "");
			if (!strcmp(buf.start, "verbose"))
				lgl->opts->verbose.val = val;
			lglprt(lgl, 1, "--%s=%d\n", buf.start, val);
			lglsetopt(lgl, buf.start, val);
		}
		goto SKIP;
	}
	DEL(buf.start, buf.size);
	if (lgl->opts->verbose.val >= 0) {
		if (embedded)
			lglprt(lgl, 1, "");
		else
			lglprt(lgl, 1, "no embedded options");
	}
	header = section = m = n = v = l = c = 0;
	if (force) {
		if (ch == 'p') {
			lglprt(lgl, 1, "will not read header");
			while ((ch = lglnextch(in, lp)) != '\n' && ch != EOF)
				;
		} else
			lglprt(lgl, 1, "skipping missing header");
		goto BODY2;
	}
	if (ch != 'p')
		return "missing 'p ...' header";
	if (lglnextch(in, lp) != ' ')
		return "invalid header: expected ' ' after 'p'";
	while ((ch = lglnextch(in, lp)) == ' ')
		;
	if (ch != 'c')
		return "invalid header: expected 'c' after ' '";
	if (lglnextch(in, lp) != 'n')
		return "invalid header: expected 'n' after 'c'";
	if (lglnextch(in, lp) != 'f')
		return "invalid header: expected 'f' after 'n'";
	if (lglnextch(in, lp) != ' ')
		return "invalid header: expected ' ' after 'f'";
	while ((ch = lglnextch(in, lp)) == ' ')
		;
	if (!isdigit(ch))
		return "invalid header: expected digit after 'p cnf '";
	m = ch - '0';
	while (isdigit(ch = lglnextch(in, lp)))
		m = 10 * m + (ch - '0');
	if (ch != ' ')
		return "invalid header: expected ' ' after 'p cnf <m>'";
	while ((ch = lglnextch(in, lp)) == ' ')
		;
	if (!isdigit(ch))
		return "invalid header: expected digit after 'p cnf <m> '";
	n = ch - '0';
	while (isdigit(ch = lglnextch(in, lp)))
		n = 10 * n + (ch - '0');
	while (ch == ' ')
		ch = lglnextch(in, lp);
	if (ch == '\r')
		ch = lglnextch(in, lp);
	if (ch != '\n')
		return "invalid header: expected new line after header";
	lglprt(lgl, 1, "found 'p cnf %d %d' header", m, n);
	*mp = m;
	header = 1;
BODY:
	ch = lglnextch(in, lp);
BODY2:
	if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
		goto BODY;
	if (ch == 'c') {
		while ((ch = lglnextch(in, lp)) != '\n' && ch != EOF)
			;
		if (ch != EOF)
			goto BODY;
	}
	if (ch == 'o') {
		if (section)
			return "two section headers in a row";
		section = ch;
		goto BODY;
	}
	if (ch == EOF) {
		if (header && c + 1 == n)
			return "clause missing";
		if (header && c < n)
			return "clauses missing";
	DONE:
		lglprt(lgl, 1, "read %d variables, %d clauses, %d literals in %.2f seconds", v, c, l, lglsec(lgl));
		return 0;
	}
	if (ch == '-') {
		ch = lglnextch(in, lp);
		if (ch == '0')
			return "expected positive digit after '-'";
		sign = -1;
	} else
		sign = 1;
	if (!isdigit(ch))
		return "expected digit";
	if (header && !section && c == n)
		return "too many clauses";
	lit = ch - '0';
	while (isdigit(ch = lglnextch(in, lp)))
		lit = 10 * lit + (ch - '0');
	if (header && lit > m)
		return "maxium variable index exceeded";
	if (lit > v)
		v = lit;
	if (lit)
		l++;
	else
		c++;
	lit *= sign;
	if (section) {
		assert(section == 'o'); // no other sections yet
		lglsetimportant(lgl, lit);
		section = 0;
	} else {
		lgladd(lgl, lit);
		if (!lit && force && c == n)
			goto DONE;
	}
	goto BODY;
}

const char*
lglparsepath(LGL* lgl, const char* path, int force, int* lp, int* mp)
{
	const char* res;
	size_t len;
	FILE* in;
	int clin;
	char* tmp;
	lglprt(lgl, 1, "reading DIMACS file '%s'", path);
	len = strlen(path);
	if (len >= 3 && !strcmp(path + len - 3, ".gz")) {
		lglprt(lgl, 1, "piping '%s' through 'gzip'", path);
		tmp = malloc(len + 20);
		sprintf(tmp, "gzip -c -d %s", path);
		in = popen(tmp, "r");
		if (in)
			clin = 2;
		free(tmp);
	} else if (len >= 3 && !strcmp(path + len - 3, ".xz")) {
		lglprt(lgl, 1, "piping '%s' through 'xz'", path);
		tmp = malloc(len + 20);
		sprintf(tmp, "xz -c -d  %s", path);
		in = popen(tmp, "r");
		if (in)
			clin = 2;
		free(tmp);
	} else if (len >= 4 && !strcmp(path + len - 4, ".bz2")) {
		lglprt(lgl, 1, "piping '%s' through 'bzip2'", path);
		tmp = malloc(len + 20);
		sprintf(tmp, "bzip2 -c -d %s", path);
		in = popen(tmp, "r");
		if (in)
			clin = 2;
		free(tmp);
	} else if (len >= 4 && !strcmp(path + len - 4, ".zip")) {
		lglprt(lgl, 1, "piping '%s' through 'unzip'", path);
		tmp = malloc(len + 20);
		sprintf(tmp, "unzip -p %s", path);
		in = popen(tmp, "r");
		if (in)
			clin = 2;
		free(tmp);
	} else if (len >= 3 && !strcmp(path + len - 3, ".7z")) {
		lglprt(lgl, 1, "piping '%s' through '7z'", path);
		tmp = malloc(len + 40);
		sprintf(tmp, "7z x -so %s 2>/dev/null", path);
		in = popen(tmp, "r");
		if (in)
			clin = 2;
		free(tmp);
	} else {
		in = fopen(path, "r");
		if (in)
			clin = 1;
	}
	*lp = 0;
	if (!in)
		return "can not read";
	res = lglparsefile(lgl, in, force, lp, mp);
	if (clin == 1)
		fclose(in);
	if (clin == 2)
		pclose(in);
	return res;
}
